diff --git a/api/tests/views_tests.py b/api/tests/views_tests.py index 216374e2db..bbd7f4c8b8 100644 --- a/api/tests/views_tests.py +++ b/api/tests/views_tests.py @@ -560,6 +560,49 @@ def test_patch_domainaddress_read_only_mask_type( assert get_glean_event(caplog) is None +def test_patch_domainaddress_address_fails( + prem_api_client: APIClient, premium_user: User, caplog: pytest.LogCaptureFixture +) -> None: + """PATCH should not succeed when attempting to update the address field.""" + existing = DomainAddress.objects.create(user=premium_user, address="my-new-alias") + url = reverse("domainaddress-detail", args=[existing.id]) + get_json = prem_api_client.get(url).json() + assert get_json["address"] == "my-new-alias" + response = prem_api_client.patch(url, data={"address": "my-new-edited-alias"}) + ret_data = response.json() + + assert response.status_code == 400 + assert ret_data["detail"] == "You cannot edit an existing domain address field." + assert ret_data["error_code"] == "address_not_editable" + assert get_glean_event(caplog) is None + + +def test_patch_domainaddress_addr_with_id_fails( + prem_api_client: APIClient, premium_user: User, caplog: pytest.LogCaptureFixture +) -> None: + """ + PATCH should not succeed when updating the address field and an 'id' field should have no effect on + the request because it is a read-only field + """ + + existing_alias = DomainAddress.objects.create( + user=premium_user, address="my-new-alias" + ) + + url = reverse("domainaddress-detail", args=[existing_alias.id]) + get_json = prem_api_client.get(url).json() + assert get_json["address"] == "my-new-alias" + response = prem_api_client.patch( + url, data={"id": 100, "address": "my-new-edited-alias"} + ) + ret_data = response.json() + + assert response.status_code == 400 + assert ret_data["detail"] == "You cannot edit an existing domain address field." + assert ret_data["error_code"] == "address_not_editable" + assert get_glean_event(caplog) is None + + def test_delete_domainaddress( prem_api_client: APIClient, premium_user: User, caplog: pytest.LogCaptureFixture ) -> None: diff --git a/emails/models.py b/emails/models.py index d8d2ae8244..67f3506539 100644 --- a/emails/models.py +++ b/emails/models.py @@ -686,6 +686,14 @@ class DomainAddrNeedSubdomainException(CannotMakeAddressException): status_code = 400 +class DomainAddrUpdateException(CannotMakeAddressException): + """Exception raised when attempting to edit an existing domain address field.""" + + default_code = "address_not_editable" + default_detail = "You cannot edit an existing domain address field." + status_code = 400 + + class DomainAddrUnavailableException(CannotMakeAddressException): default_code = "address_unavailable" default_detail_template = ( @@ -936,6 +944,12 @@ def save( incr_if_enabled("domainaddress.create") if self.first_emailed_at: incr_if_enabled("domainaddress.create_via_email") + else: + # The model is in an update state, do not allow 'address' field updates + existing_instance = DomainAddress.objects.get(id=self.id) + if existing_instance.address != self.address: + raise DomainAddrUpdateException() + if not user_profile.has_premium and self.block_list_emails: self.block_list_emails = False if update_fields: diff --git a/privaterelay/pending_locales/en/pending.ftl b/privaterelay/pending_locales/en/pending.ftl index 16b45b5311..ff57e8d7b6 100644 --- a/privaterelay/pending_locales/en/pending.ftl +++ b/privaterelay/pending_locales/en/pending.ftl @@ -7,3 +7,4 @@ # Variables: # $duplicate_address (string) - User-set email address that already exists api-error-duplicate-address = “{ $duplicate_address }” already exists. Please try again with a different mask name. +api-error-address-not-editable = You cannot edit an existing domain address field.