From bb913a937248c77c3dc39e54a683db5863b26c3e Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Sun, 9 Mar 2025 19:21:09 -0400 Subject: [PATCH] fixed some fields that should have been readonly in Domains and Domain Requests --- src/registrar/admin.py | 61 +++++++++++++--- src/registrar/tests/test_admin_domain.py | 91 ++++++++++++++++++++++-- 2 files changed, 136 insertions(+), 16 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 532b0b615..59d46eb55 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -411,17 +411,6 @@ class DomainInformationAdminForm(forms.ModelForm): class DomainInformationInlineForm(forms.ModelForm): """This form utilizes the custom widget for its class's ManyToMany UIs.""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - # for OMB analysts, limit portfolio dropdown to FEB portfolios - user = self.request.user if hasattr(self, 'request') else None - if user and user.groups.filter(name="omb_analysts_group").exists(): - self.fields["portfolio"].queryset = models.Portfolio.objects.filter( - Q(organization_type=DomainRequest.OrganizationChoices.FEDERAL) & - Q(federal_agency__federal_type=BranchChoices.EXECUTIVE) - ) class Meta: model = models.DomainInformation @@ -2343,6 +2332,47 @@ class DomainInformationAdmin(ListHeaderAdmin, ImportExportRegistrarModelAdmin): "is_policy_acknowledged", ] + # Read only that we'll leverage for OMB Analysts + omb_analyst_readonly_fields = [ + "federal_agency", + "creator", + "about_your_organization", + "anything_else", + "cisa_representative_first_name", + "cisa_representative_last_name", + "cisa_representative_email", + "domain_request", + "notes", + "senior_official", + "organization_type", + "organization_name", + "state_territory", + "address_line1", + "address_line2", + "city", + "zipcode", + "urbanization", + "portfolio_organization_type", + "portfolio_federal_type", + "portfolio_organization_name", + "portfolio_federal_agency", + "portfolio_state_territory", + "portfolio_address_line1", + "portfolio_address_line2", + "portfolio_city", + "portfolio_zipcode", + "portfolio_urbanization", + "organization_type", + "federal_type", + "federal_agency", + "tribe_name", + "federally_recognized_tribe", + "state_recognized_tribe", + "about_your_organization", + "portfolio", + "sub_organization", + ] + # For each filter_horizontal, init in admin js initFilterHorizontalWidget # to activate the edit/delete/view buttons filter_horizontal = ("other_contacts",) @@ -2371,6 +2401,10 @@ class DomainInformationAdmin(ListHeaderAdmin, ImportExportRegistrarModelAdmin): if request.user.has_perm("registrar.full_access_permission"): return readonly_fields + # Return restrictive Read-only fields for OMB analysts + if request.user.groups.filter(name="omb_analysts_group").exists(): + readonly_fields.extend([field for field in self.omb_analyst_readonly_fields]) + return readonly_fields # Return restrictive Read-only fields for analysts and # users who might not belong to groups readonly_fields.extend([field for field in self.analyst_readonly_fields]) @@ -2992,6 +3026,10 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportRegistrarModelAdmin): "action_needed_reason", "action_needed_reason_email", "portfolio", + "sub_organization", + "requested_suborganization", + "suborganization_city", + "suborganization_state_territory", ] autocomplete_fields = [ @@ -3619,6 +3657,7 @@ class DomainInformationInline(admin.StackedInline): fieldsets = copy.deepcopy(list(DomainInformationAdmin.fieldsets)) readonly_fields = copy.deepcopy(DomainInformationAdmin.readonly_fields) analyst_readonly_fields = copy.deepcopy(DomainInformationAdmin.analyst_readonly_fields) + omb_analyst_readonly_fields = copy.deepcopy(DomainInformationAdmin.omb_analyst_readonly_fields) autocomplete_fields = copy.deepcopy(DomainInformationAdmin.autocomplete_fields) def get_domain_managers(self, obj): diff --git a/src/registrar/tests/test_admin_domain.py b/src/registrar/tests/test_admin_domain.py index 2122c576f..6a65f5f5e 100644 --- a/src/registrar/tests/test_admin_domain.py +++ b/src/registrar/tests/test_admin_domain.py @@ -51,6 +51,7 @@ class TestDomainAdminAsStaff(MockEppLib): @classmethod def setUpClass(self): super().setUpClass() + self.superuser = create_superuser() self.staffuser = create_user() self.omb_analyst = create_omb_analyst_user() self.site = AdminSite() @@ -127,13 +128,93 @@ class TestDomainAdminAsStaff(MockEppLib): self.assertContains(response, "Save") self.assertNotContains(response, ">Delete<") # test whether fields are readonly or editable + self.assertNotContains(response, "id_domain_info-0-portfolio") + self.assertNotContains(response, "id_domain_info-0-sub_organization") + self.assertNotContains(response, "id_domain_info-0-creator") + self.assertNotContains(response, "id_domain_info-0-federal_agency") + self.assertNotContains(response, "id_domain_info-0-about_your_organization") + self.assertNotContains(response, "id_domain_info-0-anything_else") + self.assertNotContains(response, "id_domain_info-0-cisa_representative_first_name") + self.assertNotContains(response, "id_domain_info-0-cisa_representative_last_name") + self.assertNotContains(response, "id_domain_info-0-cisa_representative_email") + self.assertNotContains(response, "id_domain_info-0-domain_request") + self.assertNotContains(response, "id_domain_info-0-notes") + self.assertNotContains(response, "id_domain_info-0-senior_official") + self.assertNotContains(response, "id_domain_info-0-organization_type") + self.assertNotContains(response, "id_domain_info-0-state_territory") + self.assertNotContains(response, "id_domain_info-0-address_line1") + self.assertNotContains(response, "id_domain_info-0-address_line2") + self.assertNotContains(response, "id_domain_info-0-city") + self.assertNotContains(response, "id_domain_info-0-zipcode") + self.assertNotContains(response, "id_domain_info-0-urbanization") + self.assertNotContains(response, "id_domain_info-0-portfolio_organization_type") + self.assertNotContains(response, "id_domain_info-0-portfolio_federal_type") + self.assertNotContains(response, "id_domain_info-0-portfolio_organization_name") + self.assertNotContains(response, "id_domain_info-0-portfolio_federal_agency") + self.assertNotContains(response, "id_domain_info-0-portfolio_state_territory") + self.assertNotContains(response, "id_domain_info-0-portfolio_address_line1") + self.assertNotContains(response, "id_domain_info-0-portfolio_address_line2") + self.assertNotContains(response, "id_domain_info-0-portfolio_city") + self.assertNotContains(response, "id_domain_info-0-portfolio_zipcode") + self.assertNotContains(response, "id_domain_info-0-portfolio_urbanization") + self.assertNotContains(response, "id_domain_info-0-organization_type") + self.assertNotContains(response, "id_domain_info-0-federal_type") + self.assertNotContains(response, "id_domain_info-0-federal_agency") + self.assertNotContains(response, "id_domain_info-0-tribe_name") + self.assertNotContains(response, "id_domain_info-0-federally_recognized_tribe") + self.assertNotContains(response, "id_domain_info-0-state_recognized_tribe") + self.assertNotContains(response, "id_domain_info-0-about_your_organization") + self.assertNotContains(response, "id_domain_info-0-portfolio") + self.assertNotContains(response, "id_domain_info-0-sub_organization") + + @less_console_noise_decorator + def test_superuser_change(self): + """Ensure super user can view/edit all domains.""" + self.client.force_login(self.superuser) + response = self.client.get(reverse("admin:registrar_domain_change", args=[self.nonfebdomain.id])) + self.assertEqual(response.status_code, 200) + response = self.client.get(reverse("admin:registrar_domain_change", args=[self.febdomain.id])) + self.assertEqual(response.status_code, 200) + self.assertContains(response, self.febdomain.name) + # test portfolio dropdown + self.assertContains(response, self.portfolio.organization_name) + # test buttons + self.assertContains(response, "Manage domain") + self.assertContains(response, "Get registry status") + self.assertContains(response, "Extend expiration date") + self.assertContains(response, "Remove from registry") + self.assertContains(response, "Place hold") + self.assertContains(response, "Save") + self.assertContains(response, ">Delete<") + # test whether fields are readonly or editable + self.assertContains(response, "id_domain_info-0-portfolio") + self.assertContains(response, "id_domain_info-0-sub_organization") + self.assertContains(response, "id_domain_info-0-creator") + self.assertContains(response, "id_domain_info-0-federal_agency") + self.assertContains(response, "id_domain_info-0-about_your_organization") + self.assertContains(response, "id_domain_info-0-anything_else") + self.assertContains(response, "id_domain_info-0-cisa_representative_first_name") + self.assertContains(response, "id_domain_info-0-cisa_representative_last_name") + self.assertContains(response, "id_domain_info-0-cisa_representative_email") + self.assertContains(response, "id_domain_info-0-domain_request") + self.assertContains(response, "id_domain_info-0-notes") + self.assertContains(response, "id_domain_info-0-senior_official") + self.assertContains(response, "id_domain_info-0-organization_type") + self.assertContains(response, "id_domain_info-0-state_territory") + self.assertContains(response, "id_domain_info-0-address_line1") + self.assertContains(response, "id_domain_info-0-address_line2") + self.assertContains(response, "id_domain_info-0-city") + self.assertContains(response, "id_domain_info-0-zipcode") + self.assertContains(response, "id_domain_info-0-urbanization") + self.assertContains(response, "id_domain_info-0-organization_type") + self.assertContains(response, "id_domain_info-0-federal_type") + self.assertContains(response, "id_domain_info-0-federal_agency") + self.assertContains(response, "id_domain_info-0-tribe_name") + self.assertContains(response, "id_domain_info-0-federally_recognized_tribe") + self.assertContains(response, "id_domain_info-0-state_recognized_tribe") + self.assertContains(response, "id_domain_info-0-about_your_organization") self.assertContains(response, "id_domain_info-0-portfolio") self.assertContains(response, "id_domain_info-0-sub_organization") - self.assertNotContains(response, "id_domain_info-0-creator") - # self.assertNotContains(response, "id_email") - # self.assertContains(response, "closelink") - # self.assertNotContains(response, "Save") - # self.assertNotContains(response, "Delete") @less_console_noise_decorator def test_staff_can_see_cisa_region_federal(self):