diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md index 577dcb9d32..b22ebb6a34 100644 --- a/api/CHANGELOG.md +++ b/api/CHANGELOG.md @@ -12,6 +12,9 @@ All notable changes to the **Prowler API** are documented in this file. - Now the MANAGE_ACCOUNT permission is required to modify or read user permissions instead of MANAGE_USERS [(#8281)](https://github.com/prowler-cloud/prowler/pull/8281) - Now at least one user with MANAGE_ACCOUNT permission is required in the tenant [(#8729)](https://github.com/prowler-cloud/prowler/pull/8729) +### Fixed +- 500 error when deleting user [(#8731)](https://github.com/prowler-cloud/prowler/pull/8731) + --- ## [1.13.1] (Prowler 5.12.2) diff --git a/api/src/backend/api/tests/test_views.py b/api/src/backend/api/tests/test_views.py index 71fe3d98e6..aa2ac98820 100644 --- a/api/src/backend/api/tests/test_views.py +++ b/api/src/backend/api/tests/test_views.py @@ -13,6 +13,7 @@ from uuid import uuid4 import jwt import pytest from allauth.socialaccount.models import SocialAccount, SocialApp +from allauth.account.models import EmailAddress from botocore.exceptions import ClientError, NoCredentialsError from conftest import ( API_JSON_CONTENT_TYPE, @@ -325,6 +326,78 @@ class TestUserViewSet: assert response.status_code == status.HTTP_400_BAD_REQUEST assert User.objects.filter(id=another_user.id).exists() + def test_users_destroy_cascades_allauth_and_memberships( + self, authenticated_client, create_test_user + ): + # Create related admin-side objects (email + SocialAccount) + EmailAddress.objects.create( + user=create_test_user, + email=create_test_user.email, + primary=True, + verified=True, + ) + SocialAccount.objects.create( + user=create_test_user, provider="fake-provider", uid="uid-fake-provider" + ) + + # Sanity check pre-conditions + assert EmailAddress.objects.filter(user=create_test_user).exists() + assert SocialAccount.objects.filter(user=create_test_user).exists() + assert Membership.objects.filter(user=create_test_user).exists() + assert UserRoleRelationship.objects.filter(user=create_test_user).exists() + + # Delete current user + response = authenticated_client.delete( + reverse("user-detail", kwargs={"pk": str(create_test_user.id)}) + ) + assert response.status_code == status.HTTP_204_NO_CONTENT + + # Assert user and related objects are gone + assert not User.objects.filter(id=create_test_user.id).exists() + assert not EmailAddress.objects.filter(user_id=create_test_user.id).exists() + assert not SocialAccount.objects.filter(user_id=create_test_user.id).exists() + assert not Membership.objects.filter(user_id=create_test_user.id).exists() + assert not UserRoleRelationship.objects.filter( + user_id=create_test_user.id + ).exists() + + def test_users_destroy_with_saml_configuration_and_memberships( + self, authenticated_client, create_test_user, saml_setup + ): + # Ensure SAML configuration exists for tenant (from saml_setup fixture) + domain = saml_setup["domain"] + config = SAMLConfiguration.objects.get(email_domain=domain) + + # Attach a SAML SocialAccount to the user + SocialAccount.objects.create( + user=create_test_user, provider="saml", uid="uid-saml" + ) + + # Sanity check pre-conditions + assert SocialAccount.objects.filter( + user=create_test_user, provider="saml" + ).exists() + assert Membership.objects.filter(user=create_test_user).exists() + assert UserRoleRelationship.objects.filter(user=create_test_user).exists() + + # Delete current user + response = authenticated_client.delete( + reverse("user-detail", kwargs={"pk": str(create_test_user.id)}) + ) + assert response.status_code == status.HTTP_204_NO_CONTENT + + # Assert user-related rows are removed + assert not User.objects.filter(id=create_test_user.id).exists() + assert not SocialAccount.objects.filter(user_id=create_test_user.id).exists() + assert not Membership.objects.filter(user_id=create_test_user.id).exists() + assert not UserRoleRelationship.objects.filter( + user_id=create_test_user.id + ).exists() + + # Tenant-level SAML configuration should remain intact + assert SAMLConfiguration.objects.filter(id=config.id).exists() + assert SocialApp.objects.filter(provider="saml", client_id=domain).exists() + @pytest.mark.parametrize( "attribute_key, attribute_value, error_field", [ diff --git a/api/src/backend/api/v1/views.py b/api/src/backend/api/v1/views.py index b264c5fab2..f9d16e044e 100644 --- a/api/src/backend/api/v1/views.py +++ b/api/src/backend/api/v1/views.py @@ -811,7 +811,9 @@ class UserViewSet(BaseUserViewset): if kwargs["pk"] != str(self.request.user.id): raise ValidationError("Only the current user can be deleted.") - return super().destroy(request, *args, **kwargs) + user = self.get_object() + user.delete(using=MainRouter.admin_db) + return Response(status=status.HTTP_204_NO_CONTENT) @extend_schema( parameters=[