From 4b8f77436d5693854238ef4703e1096b446cf507 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 13 Sep 2024 13:48:47 -0600
Subject: [PATCH 1/8] Add corrected delete logic
---
src/registrar/views/domain_request.py | 6 ++++++
src/registrar/views/domain_requests_json.py | 18 +++++++++++++-----
2 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py
index 0abe6d69a..864225dcc 100644
--- a/src/registrar/views/domain_request.py
+++ b/src/registrar/views/domain_request.py
@@ -796,6 +796,12 @@ class DomainRequestDeleteView(DomainRequestPermissionDeleteView):
if status not in valid_statuses:
return False
+ # Portfolio users cannot delete their requests if they aren't permissioned to do so
+ if self.request.user.is_org_user(self.request):
+ portfolio = self.request.session.get("portfolio")
+ if not self.request.user.has_edit_request_portfolio_permission(portfolio):
+ return False
+
return True
def get_success_url(self):
diff --git a/src/registrar/views/domain_requests_json.py b/src/registrar/views/domain_requests_json.py
index 7b86cd9ef..bc880cdaf 100644
--- a/src/registrar/views/domain_requests_json.py
+++ b/src/registrar/views/domain_requests_json.py
@@ -25,9 +25,8 @@ def get_domain_requests_json(request):
paginator = Paginator(objects, 10)
page_number = request.GET.get("page", 1)
page_obj = paginator.get_page(page_number)
-
domain_requests = [
- serialize_domain_request(domain_request, request.user) for domain_request in page_obj.object_list
+ serialize_domain_request(request, domain_request, request.user) for domain_request in page_obj.object_list
]
return JsonResponse(
@@ -90,13 +89,22 @@ def apply_sorting(queryset, request):
return queryset.order_by(sort_by)
-def serialize_domain_request(domain_request, user):
- # Determine if the request is deletable
- is_deletable = domain_request.status in [
+def serialize_domain_request(request, domain_request, user):
+
+ deletable_statuses = [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
+ # Determine if the request is deletable
+ if not user.is_org_user(request):
+ is_deletable = domain_request.status in deletable_statuses
+ else:
+ portfolio = request.session.get("portfolio")
+ is_deletable = (
+ domain_request.status in deletable_statuses and user.has_edit_request_portfolio_permission(portfolio)
+ ) and domain_request.creator == user
+
# Determine action label based on user permissions and request status
editable_statuses = [
DomainRequest.DomainRequestStatus.STARTED,
From 939f94a4c7c8b3d565932723c64af4ee1b120604 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 13 Sep 2024 13:51:14 -0600
Subject: [PATCH 2/8] fix spacing issue
---
src/registrar/templates/portfolio_requests.html | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/registrar/templates/portfolio_requests.html b/src/registrar/templates/portfolio_requests.html
index 868c9bd2a..70d17feae 100644
--- a/src/registrar/templates/portfolio_requests.html
+++ b/src/registrar/templates/portfolio_requests.html
@@ -12,10 +12,11 @@
Domain requests
-
-
Domain requests can only be modified by the person who created the request.
-
+
{% if has_edit_request_portfolio_permission %}
+
+
Domain requests can only be modified by the person who created the request.
+
{% comment %}
IMPORTANT:
@@ -29,6 +30,8 @@
+ {% else %}
+
Domain requests can only be modified by the person who created the request.
{% endif %}
From b0b08dc74a34f78a9b99f939429c66bb3292ac91 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 13 Sep 2024 14:26:45 -0600
Subject: [PATCH 3/8] unit tests
---
src/registrar/tests/test_views_portfolio.py | 96 +++++++++++++++++++++
1 file changed, 96 insertions(+)
diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py
index aaafa3262..a960b2fd2 100644
--- a/src/registrar/tests/test_views_portfolio.py
+++ b/src/registrar/tests/test_views_portfolio.py
@@ -837,3 +837,99 @@ class TestPortfolio(WebTest):
response = self.client.get(reverse("home"))
self.assertIsNone(self.client.session.get("portfolio"))
self.assertNotContains(response, "Hotel California")
+
+ @less_console_noise_decorator
+ @override_flag("organization_feature", active=True)
+ @override_flag("organization_requests", active=True)
+ def test_delete_domain_request_as_org_user_with_permission_and_deletable_status(self):
+ """Test that an org user with edit permission can delete their own DomainRequest with a deletable status."""
+
+ # Assign the user to a portfolio with edit permission
+ UserPortfolioPermission.objects.get_or_create(
+ user=self.user,
+ portfolio=self.portfolio,
+ roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
+ additional_permissions=[UserPortfolioPermissionChoices.EDIT_REQUESTS]
+ )
+
+ # Create a domain request with status WITHDRAWN
+ domain_request = completed_domain_request(
+ name="test-domain.gov",
+ status=DomainRequest.DomainRequestStatus.WITHDRAWN,
+ portfolio=self.portfolio,
+ )
+
+ self.client.force_login(self.user)
+ # Perform delete
+ response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)
+
+ # Check that the response is 200
+ self.assertEqual(response.status_code, 200)
+
+ # Check that the domain request no longer exists
+ self.assertFalse(DomainRequest.objects.filter(pk=domain_request.pk).exists())
+
+ @less_console_noise_decorator
+ @override_flag("organization_feature", active=True)
+ @override_flag("organization_requests", active=True)
+ def test_delete_domain_request_as_org_user_without_permission_with_deletable_status(self):
+ """Test that an org user without edit permission cannot delete their DomainRequest even if status is deletable."""
+
+ # Assign the user to a portfolio without edit permission
+ UserPortfolioPermission.objects.get_or_create(
+ user=self.user,
+ portfolio=self.portfolio,
+ roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
+ additional_permissions=[]
+ )
+
+ # Create a domain request with status STARTED
+ domain_request = completed_domain_request(
+ name="test-domain.gov",
+ status=DomainRequest.DomainRequestStatus.STARTED,
+ portfolio=self.portfolio,
+ )
+
+ self.client.force_login(self.user)
+ # Attempt to delete
+ response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)
+
+ # Check response is 403 Forbidden
+ self.assertEqual(response.status_code, 403)
+
+ # Check that the domain request still exists
+ self.assertTrue(DomainRequest.objects.filter(pk=domain_request.pk).exists())
+
+ @less_console_noise_decorator
+ @override_flag("organization_feature", active=True)
+ @override_flag("organization_requests", active=True)
+ def test_delete_domain_request_as_org_user_not_creator_with_permission_and_deletable_status(self):
+ """Test that an org user with edit permission cannot delete DomainRequests they did not create."""
+
+ # Assign the user to a portfolio with edit permission
+ UserPortfolioPermission.objects.get_or_create(
+ user=self.user,
+ portfolio=self.portfolio,
+ roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
+ additional_permissions=[UserPortfolioPermissionChoices.EDIT_REQUESTS]
+ )
+
+ # Create another user and a domain request
+ other_user = User.objects.create(username="other_user")
+ domain_request = completed_domain_request(
+ name="test-domain.gov",
+ status=DomainRequest.DomainRequestStatus.STARTED,
+ portfolio=self.portfolio,
+ )
+ domain_request.creator = other_user
+ domain_request.save()
+
+ self.client.force_login(self.user)
+ # Perform delete as self.user
+ response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)
+
+ # Check response is 403 Forbidden
+ self.assertEqual(response.status_code, 403)
+
+ # Check that the domain request still exists
+ self.assertTrue(DomainRequest.objects.filter(pk=domain_request.pk).exists())
\ No newline at end of file
From 8d58b38900e8c1454fd7095206e50897d2ebeed3 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 13 Sep 2024 14:27:56 -0600
Subject: [PATCH 4/8] Update test_views_portfolio.py
---
src/registrar/tests/test_views_portfolio.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py
index a960b2fd2..6d50c9b0a 100644
--- a/src/registrar/tests/test_views_portfolio.py
+++ b/src/registrar/tests/test_views_portfolio.py
@@ -858,6 +858,8 @@ class TestPortfolio(WebTest):
status=DomainRequest.DomainRequestStatus.WITHDRAWN,
portfolio=self.portfolio,
)
+ domain_request.creator = self.user
+ domain_request.save()
self.client.force_login(self.user)
# Perform delete
@@ -889,6 +891,8 @@ class TestPortfolio(WebTest):
status=DomainRequest.DomainRequestStatus.STARTED,
portfolio=self.portfolio,
)
+ domain_request.creator = self.user
+ domain_request.save()
self.client.force_login(self.user)
# Attempt to delete
From a03769350ac4ec713db0ba332e27b80fa28eeeae Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 13 Sep 2024 14:29:28 -0600
Subject: [PATCH 5/8] lint
---
src/registrar/tests/test_views_portfolio.py | 8 ++++----
src/registrar/views/domain_request.py | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py
index 6d50c9b0a..a952da5e7 100644
--- a/src/registrar/tests/test_views_portfolio.py
+++ b/src/registrar/tests/test_views_portfolio.py
@@ -849,7 +849,7 @@ class TestPortfolio(WebTest):
user=self.user,
portfolio=self.portfolio,
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
- additional_permissions=[UserPortfolioPermissionChoices.EDIT_REQUESTS]
+ additional_permissions=[UserPortfolioPermissionChoices.EDIT_REQUESTS],
)
# Create a domain request with status WITHDRAWN
@@ -882,7 +882,7 @@ class TestPortfolio(WebTest):
user=self.user,
portfolio=self.portfolio,
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
- additional_permissions=[]
+ additional_permissions=[],
)
# Create a domain request with status STARTED
@@ -915,7 +915,7 @@ class TestPortfolio(WebTest):
user=self.user,
portfolio=self.portfolio,
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
- additional_permissions=[UserPortfolioPermissionChoices.EDIT_REQUESTS]
+ additional_permissions=[UserPortfolioPermissionChoices.EDIT_REQUESTS],
)
# Create another user and a domain request
@@ -936,4 +936,4 @@ class TestPortfolio(WebTest):
self.assertEqual(response.status_code, 403)
# Check that the domain request still exists
- self.assertTrue(DomainRequest.objects.filter(pk=domain_request.pk).exists())
\ No newline at end of file
+ self.assertTrue(DomainRequest.objects.filter(pk=domain_request.pk).exists())
diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py
index 864225dcc..5fed89215 100644
--- a/src/registrar/views/domain_request.py
+++ b/src/registrar/views/domain_request.py
@@ -796,7 +796,7 @@ class DomainRequestDeleteView(DomainRequestPermissionDeleteView):
if status not in valid_statuses:
return False
- # Portfolio users cannot delete their requests if they aren't permissioned to do so
+ # Portfolio users cannot delete their requests if they aren't permissioned to do so
if self.request.user.is_org_user(self.request):
portfolio = self.request.session.get("portfolio")
if not self.request.user.has_edit_request_portfolio_permission(portfolio):
From e078248e02e73e45023df72147ee54227a932650 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 13 Sep 2024 14:32:57 -0600
Subject: [PATCH 6/8] Update test_views_portfolio.py
---
src/registrar/tests/test_views_portfolio.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py
index a952da5e7..edac1d783 100644
--- a/src/registrar/tests/test_views_portfolio.py
+++ b/src/registrar/tests/test_views_portfolio.py
@@ -875,7 +875,7 @@ class TestPortfolio(WebTest):
@override_flag("organization_feature", active=True)
@override_flag("organization_requests", active=True)
def test_delete_domain_request_as_org_user_without_permission_with_deletable_status(self):
- """Test that an org user without edit permission cannot delete their DomainRequest even if status is deletable."""
+ """Test that an org user without edit permission cant delete their DomainRequest even if status is deletable."""
# Assign the user to a portfolio without edit permission
UserPortfolioPermission.objects.get_or_create(
From 897fd86e5753fa7c5f9d0174d93cf64c393a6725 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 13 Sep 2024 14:36:15 -0600
Subject: [PATCH 7/8] add delete
---
src/registrar/tests/test_views_portfolio.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py
index edac1d783..e238ea0d6 100644
--- a/src/registrar/tests/test_views_portfolio.py
+++ b/src/registrar/tests/test_views_portfolio.py
@@ -870,6 +870,7 @@ class TestPortfolio(WebTest):
# Check that the domain request no longer exists
self.assertFalse(DomainRequest.objects.filter(pk=domain_request.pk).exists())
+ domain_request.delete()
@less_console_noise_decorator
@override_flag("organization_feature", active=True)
@@ -903,6 +904,7 @@ class TestPortfolio(WebTest):
# Check that the domain request still exists
self.assertTrue(DomainRequest.objects.filter(pk=domain_request.pk).exists())
+ domain_request.delete()
@less_console_noise_decorator
@override_flag("organization_feature", active=True)
@@ -937,3 +939,4 @@ class TestPortfolio(WebTest):
# Check that the domain request still exists
self.assertTrue(DomainRequest.objects.filter(pk=domain_request.pk).exists())
+ domain_request.delete()
From ef40468ec847403fde3e02a04d1e7d6c0f936be4 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Tue, 17 Sep 2024 08:32:51 -0600
Subject: [PATCH 8/8] Rename tests to be actually readable
---
src/registrar/tests/test_views_portfolio.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py
index e238ea0d6..b8392a370 100644
--- a/src/registrar/tests/test_views_portfolio.py
+++ b/src/registrar/tests/test_views_portfolio.py
@@ -841,7 +841,7 @@ class TestPortfolio(WebTest):
@less_console_noise_decorator
@override_flag("organization_feature", active=True)
@override_flag("organization_requests", active=True)
- def test_delete_domain_request_as_org_user_with_permission_and_deletable_status(self):
+ def test_org_user_can_delete_own_domain_request_with_permission(self):
"""Test that an org user with edit permission can delete their own DomainRequest with a deletable status."""
# Assign the user to a portfolio with edit permission
@@ -909,7 +909,7 @@ class TestPortfolio(WebTest):
@less_console_noise_decorator
@override_flag("organization_feature", active=True)
@override_flag("organization_requests", active=True)
- def test_delete_domain_request_as_org_user_not_creator_with_permission_and_deletable_status(self):
+ def test_org_user_cannot_delete_others_domain_requests(self):
"""Test that an org user with edit permission cannot delete DomainRequests they did not create."""
# Assign the user to a portfolio with edit permission