From 67d177dc75b5b9881ee67fc796462e95894cea93 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:18:38 -0600 Subject: [PATCH 01/32] #1102 - Changed disclose behaviour --- src/registrar/models/domain.py | 15 ++++++--------- src/registrar/tests/common.py | 8 ++------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index e45724a9b..744c54a56 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -850,18 +850,15 @@ class Domain(TimeStampedModel, DomainHelper): """creates a disclose object that can be added to a contact Create using .disclose= on the command before sending. if item is security email then make sure email is visable""" - isSecurity = contact.contact_type == contact.ContactTypeChoices.SECURITY + is_security = contact.contact_type == contact.ContactTypeChoices.SECURITY DF = epp.DiscloseField - fields = {DF.FAX, DF.VOICE, DF.ADDR} - - if not isSecurity or ( - isSecurity and contact.email == PublicContact.get_default_security().email - ): - fields.add(DF.EMAIL) + fields = {DF.EMAIL} + disclose = ( + is_security and contact.email != PublicContact.get_default_security().email + ) return epp.Disclose( - flag=False, + flag=disclose, fields=fields, - types={DF.ADDR: "loc"}, ) def _make_epp_contact_postal_info(self, contact: PublicContact): # type: ignore diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 10c387099..3770bbe09 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -626,15 +626,11 @@ class MockEppLib(TestCase): self, contact: PublicContact, disclose_email=False, createContact=True ): DF = common.DiscloseField - fields = {DF.FAX, DF.VOICE, DF.ADDR} - - if not disclose_email: - fields.add(DF.EMAIL) + fields = {DF.EMAIL} di = common.Disclose( - flag=False, + flag=disclose_email, fields=fields, - types={DF.ADDR: "loc"}, ) # check docs here looks like we may have more than one address field but From 914b2da187e7daa87fd12231363900af74306bb4 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:36:56 -0600 Subject: [PATCH 02/32] Update domain.py --- src/registrar/models/domain.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 744c54a56..2d75da42c 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -856,6 +856,7 @@ class Domain(TimeStampedModel, DomainHelper): disclose = ( is_security and contact.email != PublicContact.get_default_security().email ) + # Will only disclose DF.EMAIL if its not the default return epp.Disclose( flag=disclose, fields=fields, From fb5faf35f1c6d7b1a207ccd963e815f746e8f59a Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 4 Oct 2023 10:50:07 -0600 Subject: [PATCH 03/32] Added tests --- src/registrar/tests/test_models_domain.py | 96 +++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index bf258db31..9068863b0 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -629,6 +629,102 @@ class TestRegistrantContacts(MockEppLib): self.mockedSendFunction.assert_has_calls(expected_calls, any_order=True) self.assertEqual(PublicContact.objects.filter(domain=self.domain).count(), 1) + @skip("Dependent on #850") + def test_not_disclosed_on_other_contacts(self): + """ + Scenario: Registrant creates a new domain with multiple contacts + When `domain` has registrant, admin, technical, + and security contacts + Then Domain sends `commands.CreateContact` to the registry + And the field `disclose` is set to false for DF.EMAIL + on all fields except security + """ + # Generates a domain with four existing contacts + domain, _ = Domain.objects.get_or_create(name="igorville.gov") + # Adds default emails to all fields + domain.addAllDefaults() + # Security contact should be disclosed + domain.security_contact.email = "test123@mail.gov" + # TODO - uncomment below when #850 is merged + domain.registrant_contact = domain.get_default_registrant_contact() + + expected_admin = domain.get_default_administrative_contact() + expected_registrant = domain.get_default_registrant_contact() + expected_security = domain.get_default_security_contact() + expected_tech = domain.get_default_technical_contact() + + contacts = [ + expected_admin, + expected_registrant, + expected_security, + expected_tech + ] + + for contact in contacts: + id = PublicContact.objects.get( + domain=self.domain, + contact_type=contact.contact_type_choice, + ).registry_id + contact.registry_id = id + + expectedCreateCommand = self._convertPublicContactToEpp( + contact, disclose_email=False + ) + + self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) + + @skip("Dependent on #850") + def test_not_disclosed_on_default_security_contact(self): + """ + Scenario: Registrant creates a new domain with no security email + When `domain.security_contact.email` is equal to the default + Then Domain sends `commands.CreateContact` to the registry + And the field `disclose` is set to false for DF.EMAIL + """ + expectedSecContact = PublicContact.get_default_security() + expectedSecContact.domain = self.domain + self.domain.security_contact.email = "test123@mail.gov" + + id = PublicContact.objects.get( + domain=self.domain, + contact_type=PublicContact.ContactTypeChoices.SECURITY, + ).registry_id + + expectedSecContact.registry_id = id + expectedCreateCommand = self._convertPublicContactToEpp( + expectedSecContact, disclose_email=True + ) + + self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) + # Confirm that we are getting a default object + self.assertEqual(self.domain.security_contact, expectedSecContact) + + @skip("Dependent on #850") + def test_is_disclosed_on_security_contact(self): + """ + Scenario: Registrant creates a new domain with a security email + When `domain.security_contact.email` is set to a valid email + and is not the default + Then Domain sends `commands.CreateContact` to the registry + And the field `disclose` is set to true for DF.EMAIL + """ + expectedSecContact = PublicContact.get_default_security() + expectedSecContact.domain = self.domain + + id = PublicContact.objects.get( + domain=self.domain, + contact_type=PublicContact.ContactTypeChoices.SECURITY, + ).registry_id + + expectedSecContact.registry_id = id + expectedCreateCommand = self._convertPublicContactToEpp( + expectedSecContact, disclose_email=False + ) + + self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) + # Confirm that we are getting a default object + self.assertEqual(self.domain.security_contact, expectedSecContact) + @skip("not implemented yet") def test_update_is_unsuccessful(self): """ From bb1e7bdf78b883224d9479afb57f6b8d0ebe58fe Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 4 Oct 2023 12:23:08 -0600 Subject: [PATCH 04/32] Test cases --- src/registrar/tests/common.py | 23 +++++++++ src/registrar/tests/test_models_domain.py | 63 +++++++++-------------- 2 files changed, 48 insertions(+), 38 deletions(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 7a98f224d..1257abe29 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -647,6 +647,25 @@ class MockEppLib(TestCase): registrant="regContact", ) + InfoDomainWithDefaultSecurityContact = fakedEppObject( + "fakepw", + cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35), + contacts=[ + common.DomainContact( + contact="defaultSec", + type=PublicContact.ContactTypeChoices.SECURITY, + ) + ], + hosts=["fake.host.com"], + statuses=[ + common.Status(state="serverTransferProhibited", description="", lang="en"), + common.Status(state="inactive", description="", lang="en"), + ], + ) + + mockDefaultSecurityContact = InfoDomainWithContacts.dummyInfoContactResultData( + "defaultSec", "dotgov@cisa.dhs.gov" + ) mockSecurityContact = InfoDomainWithContacts.dummyInfoContactResultData( "securityContact", "security@mail.gov" ) @@ -681,6 +700,8 @@ class MockEppLib(TestCase): return MagicMock(res_data=[self.infoDomainNoContact]) elif getattr(_request, "name", None) == "freeman.gov": return MagicMock(res_data=[self.InfoDomainWithContacts]) + elif getattr(_request, "name", None) == "defaultsecurity.gov": + return MagicMock(res_data=[self.InfoDomainWithDefaultSecurityContact]) else: return MagicMock(res_data=[self.mockDataInfoDomain]) elif isinstance(_request, commands.InfoContact): @@ -696,6 +717,8 @@ class MockEppLib(TestCase): mocked_result = self.mockAdministrativeContact case "regContact": mocked_result = self.mockRegistrantContact + case "defaultSec": + mocked_result = self.mockDefaultSecurityContact case _: # Default contact return mocked_result = self.mockDataInfoContact diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 51748fdfd..b61e3551e 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -705,7 +705,6 @@ class TestRegistrantContacts(MockEppLib): self.mockedSendFunction.assert_has_calls(expected_calls, any_order=True) self.assertEqual(PublicContact.objects.filter(domain=self.domain).count(), 1) - @skip("Dependent on #850") def test_not_disclosed_on_other_contacts(self): """ Scenario: Registrant creates a new domain with multiple contacts @@ -716,19 +715,19 @@ class TestRegistrantContacts(MockEppLib): on all fields except security """ # Generates a domain with four existing contacts - domain, _ = Domain.objects.get_or_create(name="igorville.gov") - # Adds default emails to all fields - domain.addAllDefaults() - # Security contact should be disclosed - domain.security_contact.email = "test123@mail.gov" - # TODO - uncomment below when #850 is merged - domain.registrant_contact = domain.get_default_registrant_contact() + domain, _ = Domain.objects.get_or_create(name="freeman.gov") expected_admin = domain.get_default_administrative_contact() expected_registrant = domain.get_default_registrant_contact() expected_security = domain.get_default_security_contact() + expected_security.email = "security@mail.gov" expected_tech = domain.get_default_technical_contact() + domain.administrative_contact = expected_admin + domain.registrant_contact = expected_registrant + domain.security_contact = expected_security + domain.technical_contact = expected_tech + contacts = [ expected_admin, expected_registrant, @@ -737,19 +736,14 @@ class TestRegistrantContacts(MockEppLib): ] for contact in contacts: - id = PublicContact.objects.get( - domain=self.domain, - contact_type=contact.contact_type_choice, - ).registry_id - contact.registry_id = id - + is_security = contact.contact_type == "security" expectedCreateCommand = self._convertPublicContactToEpp( - contact, disclose_email=False + contact, + disclose_email=is_security ) self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) - @skip("Dependent on #850") def test_not_disclosed_on_default_security_contact(self): """ Scenario: Registrant creates a new domain with no security email @@ -757,25 +751,21 @@ class TestRegistrantContacts(MockEppLib): Then Domain sends `commands.CreateContact` to the registry And the field `disclose` is set to false for DF.EMAIL """ + self.maxDiff = None + domain, _ = Domain.objects.get_or_create(name="defaultsecurity.gov") expectedSecContact = PublicContact.get_default_security() - expectedSecContact.domain = self.domain - self.domain.security_contact.email = "test123@mail.gov" + expectedSecContact.domain = domain + expectedSecContact.registry_id="defaultSec" + domain.security_contact = expectedSecContact - id = PublicContact.objects.get( - domain=self.domain, - contact_type=PublicContact.ContactTypeChoices.SECURITY, - ).registry_id - - expectedSecContact.registry_id = id expectedCreateCommand = self._convertPublicContactToEpp( - expectedSecContact, disclose_email=True + expectedSecContact, disclose_email=False ) self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) - # Confirm that we are getting a default object - self.assertEqual(self.domain.security_contact, expectedSecContact) + # Confirm that we are getting a default email + self.assertEqual(domain.security_contact.email, expectedSecContact.email) - @skip("Dependent on #850") def test_is_disclosed_on_security_contact(self): """ Scenario: Registrant creates a new domain with a security email @@ -784,22 +774,19 @@ class TestRegistrantContacts(MockEppLib): Then Domain sends `commands.CreateContact` to the registry And the field `disclose` is set to true for DF.EMAIL """ + domain, _ = Domain.objects.get_or_create(name="igorville.gov") expectedSecContact = PublicContact.get_default_security() - expectedSecContact.domain = self.domain + expectedSecContact.domain = domain + expectedSecContact.email = "123@mail.gov" + domain.security_contact = expectedSecContact - id = PublicContact.objects.get( - domain=self.domain, - contact_type=PublicContact.ContactTypeChoices.SECURITY, - ).registry_id - - expectedSecContact.registry_id = id expectedCreateCommand = self._convertPublicContactToEpp( - expectedSecContact, disclose_email=False + expectedSecContact, disclose_email=True ) self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) - # Confirm that we are getting a default object - self.assertEqual(self.domain.security_contact, expectedSecContact) + # Confirm that we are getting the desired email + self.assertEqual(domain.security_contact.email, expectedSecContact.email) @skip("not implemented yet") def test_update_is_unsuccessful(self): From f0acb978b995a307180dbfc50602e98cccdf37ba Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 4 Oct 2023 12:25:16 -0600 Subject: [PATCH 05/32] Running black for formatting --- src/registrar/tests/test_models_domain.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index b61e3551e..666d93c93 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -708,7 +708,7 @@ class TestRegistrantContacts(MockEppLib): def test_not_disclosed_on_other_contacts(self): """ Scenario: Registrant creates a new domain with multiple contacts - When `domain` has registrant, admin, technical, + When `domain` has registrant, admin, technical, and security contacts Then Domain sends `commands.CreateContact` to the registry And the field `disclose` is set to false for DF.EMAIL @@ -732,16 +732,15 @@ class TestRegistrantContacts(MockEppLib): expected_admin, expected_registrant, expected_security, - expected_tech + expected_tech, ] for contact in contacts: is_security = contact.contact_type == "security" expectedCreateCommand = self._convertPublicContactToEpp( - contact, - disclose_email=is_security + contact, disclose_email=is_security ) - + self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) def test_not_disclosed_on_default_security_contact(self): @@ -755,13 +754,13 @@ class TestRegistrantContacts(MockEppLib): domain, _ = Domain.objects.get_or_create(name="defaultsecurity.gov") expectedSecContact = PublicContact.get_default_security() expectedSecContact.domain = domain - expectedSecContact.registry_id="defaultSec" + expectedSecContact.registry_id = "defaultSec" domain.security_contact = expectedSecContact expectedCreateCommand = self._convertPublicContactToEpp( expectedSecContact, disclose_email=False ) - + self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) # Confirm that we are getting a default email self.assertEqual(domain.security_contact.email, expectedSecContact.email) @@ -783,7 +782,7 @@ class TestRegistrantContacts(MockEppLib): expectedCreateCommand = self._convertPublicContactToEpp( expectedSecContact, disclose_email=True ) - + self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) # Confirm that we are getting the desired email self.assertEqual(domain.security_contact.email, expectedSecContact.email) From b824b6725fc7e0731405187ab8bc27dc73a938b4 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 4 Oct 2023 12:46:05 -0600 Subject: [PATCH 06/32] Test for technical contact on its own --- src/registrar/tests/common.py | 23 +++++++++++++++++++++++ src/registrar/tests/test_models_domain.py | 22 +++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 1257abe29..20c08b9f4 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -663,6 +663,25 @@ class MockEppLib(TestCase): ], ) + InfoDomainWithDefaultTechnicalContact = fakedEppObject( + "fakepw", + cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35), + contacts=[ + common.DomainContact( + contact="defaultTech", + type=PublicContact.ContactTypeChoices.TECHNICAL, + ) + ], + hosts=["fake.host.com"], + statuses=[ + common.Status(state="serverTransferProhibited", description="", lang="en"), + common.Status(state="inactive", description="", lang="en"), + ], + ) + + mockDefaultTechnicalContact = InfoDomainWithContacts.dummyInfoContactResultData( + "defaultTech", "dotgov@cisa.dhs.gov" + ) mockDefaultSecurityContact = InfoDomainWithContacts.dummyInfoContactResultData( "defaultSec", "dotgov@cisa.dhs.gov" ) @@ -702,6 +721,8 @@ class MockEppLib(TestCase): return MagicMock(res_data=[self.InfoDomainWithContacts]) elif getattr(_request, "name", None) == "defaultsecurity.gov": return MagicMock(res_data=[self.InfoDomainWithDefaultSecurityContact]) + elif getattr(_request, "name", None) == "defaulttechnical.gov": + return MagicMock(res_data=[self.InfoDomainWithDefaultTechnicalContact]) else: return MagicMock(res_data=[self.mockDataInfoDomain]) elif isinstance(_request, commands.InfoContact): @@ -719,6 +740,8 @@ class MockEppLib(TestCase): mocked_result = self.mockRegistrantContact case "defaultSec": mocked_result = self.mockDefaultSecurityContact + case "defaultTech": + mocked_result = self.mockDefaultTechnicalContact case _: # Default contact return mocked_result = self.mockDataInfoContact diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 666d93c93..296389808 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -750,7 +750,6 @@ class TestRegistrantContacts(MockEppLib): Then Domain sends `commands.CreateContact` to the registry And the field `disclose` is set to false for DF.EMAIL """ - self.maxDiff = None domain, _ = Domain.objects.get_or_create(name="defaultsecurity.gov") expectedSecContact = PublicContact.get_default_security() expectedSecContact.domain = domain @@ -765,6 +764,27 @@ class TestRegistrantContacts(MockEppLib): # Confirm that we are getting a default email self.assertEqual(domain.security_contact.email, expectedSecContact.email) + def test_not_disclosed_on_default_technical_contact(self): + """ + Scenario: Registrant creates a new domain with no technical contact + When `domain.technical_contact.email` is equal to the default + Then Domain sends `commands.CreateContact` to the registry + And the field `disclose` is set to false for DF.EMAIL + """ + domain, _ = Domain.objects.get_or_create(name="defaulttechnical.gov") + expectedTechContact = PublicContact.get_default_technical() + expectedTechContact.domain = domain + expectedTechContact.registry_id = "defaultTech" + domain.technical_contact = expectedTechContact + + expectedCreateCommand = self._convertPublicContactToEpp( + expectedTechContact, disclose_email=False + ) + + self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) + # Confirm that we are getting a default email + self.assertEqual(domain.technical_contact.email, expectedTechContact.email) + def test_is_disclosed_on_security_contact(self): """ Scenario: Registrant creates a new domain with a security email From bb193bd500371832f3eec509fbc767e89a32da6c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 4 Oct 2023 14:15:36 -0600 Subject: [PATCH 07/32] Added comment --- src/registrar/tests/test_models_domain.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 296389808..0c75ddb88 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -741,6 +741,7 @@ class TestRegistrantContacts(MockEppLib): contact, disclose_email=is_security ) + # Should only be disclosed if the type is security self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) def test_not_disclosed_on_default_security_contact(self): From c788200ed3751d05c565083f96cc91dc47568621 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 4 Oct 2023 15:59:13 -0600 Subject: [PATCH 08/32] Update test_models_domain.py --- src/registrar/tests/test_models_domain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 0c75ddb88..0e0db2810 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -741,7 +741,7 @@ class TestRegistrantContacts(MockEppLib): contact, disclose_email=is_security ) - # Should only be disclosed if the type is security + # Should only be disclosed if the type is security, as the email is valid self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) def test_not_disclosed_on_default_security_contact(self): From 9ad183712b23d960c60d49f3a27a0bb41af7b916 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:59:46 -0600 Subject: [PATCH 09/32] Test on emails --- src/registrar/tests/test_models_domain.py | 28 +++++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 0e0db2810..7cfc6e4b4 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -717,11 +717,18 @@ class TestRegistrantContacts(MockEppLib): # Generates a domain with four existing contacts domain, _ = Domain.objects.get_or_create(name="freeman.gov") + # Contact setup expected_admin = domain.get_default_administrative_contact() + expected_admin.email = self.mockAdministrativeContact.email + expected_registrant = domain.get_default_registrant_contact() + expected_registrant.email = self.mockRegistrantContact.email + expected_security = domain.get_default_security_contact() - expected_security.email = "security@mail.gov" + expected_security.email = self.mockSecurityContact.email + expected_tech = domain.get_default_technical_contact() + expected_tech.email = self.mockTechnicalContact.email domain.administrative_contact = expected_admin domain.registrant_contact = expected_registrant @@ -729,21 +736,28 @@ class TestRegistrantContacts(MockEppLib): domain.technical_contact = expected_tech contacts = [ - expected_admin, - expected_registrant, - expected_security, - expected_tech, + (expected_admin, domain.administrative_contact), + (expected_registrant, domain.registrant_contact), + (expected_security, domain.security_contact), + (expected_tech, domain.technical_contact), ] + # Test for each contact for contact in contacts: - is_security = contact.contact_type == "security" + expected_contact = contact[0] + actual_contact = contact[1] + is_security = actual_contact.contact_type == "security" + expectedCreateCommand = self._convertPublicContactToEpp( - contact, disclose_email=is_security + expected_contact, disclose_email=is_security ) # Should only be disclosed if the type is security, as the email is valid self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) + # The emails should match on both items + self.assertEqual(expected_contact.email, actual_contact.email) + def test_not_disclosed_on_default_security_contact(self): """ Scenario: Registrant creates a new domain with no security email From e6e0c2c416857763d83fef8be032613e73fee0a0 Mon Sep 17 00:00:00 2001 From: Cameron Dixon Date: Fri, 6 Oct 2023 12:16:20 -0400 Subject: [PATCH 10/32] Content revision, add :emoji: names --- .github/ISSUE_TEMPLATE/issue-default.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/issue-default.yml b/.github/ISSUE_TEMPLATE/issue-default.yml index 27ec10415..2252845bf 100644 --- a/.github/ISSUE_TEMPLATE/issue-default.yml +++ b/.github/ISSUE_TEMPLATE/issue-default.yml @@ -6,13 +6,13 @@ body: id: title-help attributes: value: | - > Titles should be short, descriptive, and compelling. + > Titles should be short, descriptive, and compelling. Use sentence case. - type: textarea id: issue-description attributes: label: Issue description and context description: | - Describe the issue so that someone who wasn't present for its discovery can understand the problem and why it matters. Use full sentences, plain language, and good [formatting](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax). Share desired outcomes or potential next steps. Images or links to other content/context (like documents or Slack discussions) are welcome. + Describe the issue so that someone who wasn't present for its discovery can understand why it matters. Use full sentences, plain language, and good [formatting](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax). Screenshots and links to documents/discussions are welcome. validations: required: true - type: textarea @@ -20,13 +20,13 @@ body: attributes: label: Acceptance criteria description: "If known, share 1-3 statements that would need to be true for this issue to be considered resolved. Use a [task list](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/about-task-lists#creating-task-lists) if appropriate." - placeholder: "- [ ] The button does the thing." + placeholder: "- [ ]" - type: textarea id: links-to-other-issues attributes: label: Links to other issues description: | - Add the issue #number of other issues this relates to and how (e.g., 🚧 Blocks, ⛔️ Is blocked by, 🔄 Relates to). + "Add issue #numbers this relates to and how (e.g., 🚧 :construction: Blocks, ⛔️ :no_entry: Is blocked by, 🔄 :repeat: Relates to)." placeholder: 🔄 Relates to... - type: markdown id: note From ad607547a0b1bfe289c05860533ab6a7f061006a Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 11 Oct 2023 10:45:59 -0600 Subject: [PATCH 11/32] Fix typo --- src/registrar/tests/test_models_domain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 7cfc6e4b4..0165000d0 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -746,7 +746,7 @@ class TestRegistrantContacts(MockEppLib): for contact in contacts: expected_contact = contact[0] actual_contact = contact[1] - is_security = actual_contact.contact_type == "security" + is_security = expected_contact.contact_type == "security" expectedCreateCommand = self._convertPublicContactToEpp( expected_contact, disclose_email=is_security From 3ed4c0e4fbbbfdf5b69d7a6d4c1af0d5ec90a61c Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 17 Oct 2023 08:37:03 -0700 Subject: [PATCH 12/32] Update availability API to use EPP availability check --- src/! | 1 + src/api/tests/test_available.py | 78 +++++++++++++++++++++++-------- src/api/views.py | 6 +-- src/registrar/tests/common.py | 37 ++++++++++++++- src/registrar/tests/test_forms.py | 11 ++++- 5 files changed, 108 insertions(+), 25 deletions(-) create mode 100644 src/! diff --git a/src/! b/src/! new file mode 100644 index 000000000..19765bd50 --- /dev/null +++ b/src/! @@ -0,0 +1 @@ +null diff --git a/src/api/tests/test_available.py b/src/api/tests/test_available.py index 0bbe01f03..3d01228c3 100644 --- a/src/api/tests/test_available.py +++ b/src/api/tests/test_available.py @@ -7,21 +7,34 @@ from django.test import TestCase, RequestFactory from ..views import available, _domains, in_domains from .common import less_console_noise +from registrar.tests.common import MockEppLib +from unittest.mock import MagicMock, patch, call + +from epplibwrapper import ( + commands, + common, + extensions, + responses, + RegistryError, + ErrorCode, +) API_BASE_PATH = "/api/v1/available/" +from registrar.models import Domain - -class AvailableViewTest(TestCase): +class AvailableViewTest(MockEppLib): """Test that the view function works as expected.""" def setUp(self): + super().setUp() self.user = get_user_model().objects.create(username="username") self.factory = RequestFactory() def test_view_function(self): request = self.factory.get(API_BASE_PATH + "test.gov") request.user = self.user + response = available(request, domain="test.gov") # has the right text in it self.assertContains(response, "available") @@ -29,28 +42,43 @@ class AvailableViewTest(TestCase): response_object = json.loads(response.content) self.assertIn("available", response_object) - def test_domain_list(self): - """Test the domain list that is returned from Github. + def test_makes_calls(self): + gsa_available = in_domains("gsa.gov") + igorville_available = in_domains("igorvilleremixed.gov") - This does not mock out the external file, it is actually fetched from - the internet. - """ - domains = _domains() - self.assertIn("gsa.gov", domains) - # entries are all lowercase so GSA.GOV is not in the set - self.assertNotIn("GSA.GOV", domains) - self.assertNotIn("igorvilleremixed.gov", domains) - # all the entries have dots - self.assertNotIn("gsa", domains) + self.mockedSendFunction.assert_has_calls( + [ + call( + commands.CheckDomain( + ["gsa.gov"], + ), + cleaned=True, + ), + call( + commands.CheckDomain( + ["igorvilleremixed.gov"], + ), + cleaned=True, + ) + ] + ) def test_in_domains(self): - self.assertTrue(in_domains("gsa.gov")) + gsa_available = in_domains("gsa.gov") + gsa_caps_available = in_domains("GSA.gov") + igorville_available = in_domains("igorvilleremixed.gov") + + self.assertTrue(gsa_available) # input is lowercased so GSA.GOV should be found - self.assertTrue(in_domains("GSA.GOV")) + self.assertTrue(gsa_caps_available) # This domain should not have been registered - self.assertFalse(in_domains("igorvilleremixed.gov")) - + self.assertFalse(igorville_available) + def test_in_domains_dotgov(self): + gsa_available = in_domains("gsa.gov") + gsa_caps_available = in_domains("GSA.gov") + igorville_available = in_domains("igorvilleremixed.gov") + """Domain searches work without trailing .gov""" self.assertTrue(in_domains("gsa")) # input is lowercased so GSA.GOV should be found @@ -58,6 +86,14 @@ class AvailableViewTest(TestCase): # This domain should not have been registered self.assertFalse(in_domains("igorvilleremixed")) + def test_in_domains_capitalized(self): + gsa_available = in_domains("gsa.gov") + capitalized_gsa_available = in_domains("GSA.gov") + + """Domain searches work without case sensitivity""" + self.assertTrue(in_domains("gsa.gov")) + self.assertTrue(in_domains("GSA.gov")) + def test_not_available_domain(self): """gsa.gov is not available""" request = self.factory.get(API_BASE_PATH + "gsa.gov") @@ -86,13 +122,17 @@ class AvailableViewTest(TestCase): request.user = self.user response = available(request, domain=bad_string) self.assertFalse(json.loads(response.content)["available"]) + # domain set to raise error successfully raises error + with self.assertRaises(RegistryError): + error_domain_available = available(request, "errordomain.gov") -class AvailableAPITest(TestCase): +class AvailableAPITest(MockEppLib): """Test that the API can be called as expected.""" def setUp(self): + super().setUp() self.user = get_user_model().objects.create(username="username") def test_available_get(self): diff --git a/src/api/views.py b/src/api/views.py index e19e060ef..02e419a91 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -59,12 +59,12 @@ def in_domains(domain): given domain doesn't end with .gov, ".gov" is added when looking for a match. """ - domain = domain.lower() + Domain = apps.get_model("registrar.Domain") if domain.endswith(".gov"): - return domain.lower() in _domains() + return Domain.available(domain) else: # domain search string doesn't end with .gov, add it on here - return (domain + ".gov") in _domains() + return Domain.available(domain + ".gov") @require_http_methods(["GET"]) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index b8fea7f93..0144738e2 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -30,6 +30,7 @@ from epplibwrapper import ( info, RegistryError, ErrorCode, + responses, ) logger = logging.getLogger(__name__) @@ -824,7 +825,41 @@ class MockEppLib(TestCase): raise RegistryError( code=ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION ) - + elif isinstance(_request, commands.CheckDomain): + if "gsa.gov" in getattr(_request, "names", None): + return MagicMock( + res_data=[ + responses.check.CheckDomainResultData( + name="gsa.gov", avail=True, reason=None + ), + ] + ) + elif "GSA.gov" in getattr(_request, "names", None): + return MagicMock( + res_data=[ + responses.check.CheckDomainResultData( + name="GSA.gov", avail=True, reason=None + ), + ] + ) + elif "igorvilleremixed.gov" in getattr(_request, "names", None): + return MagicMock( + res_data=[ + responses.check.CheckDomainResultData( + name="igorvilleremixed.gov", avail=False, reason=None + ), + ] + ) + elif "errordomain.gov" in getattr(_request, "names", None): + raise RegistryError("Registry cannot find domain availability.") + else: + return MagicMock( + res_data=[ + responses.check.CheckDomainResultData( + name="domainnotfound.gov", avail=False, reason="In Use" + ) + ], + ) return MagicMock(res_data=[self.mockDataInfoHosts]) def setUp(self): diff --git a/src/registrar/tests/test_forms.py b/src/registrar/tests/test_forms.py index 95be195ba..4b1aeb12c 100644 --- a/src/registrar/tests/test_forms.py +++ b/src/registrar/tests/test_forms.py @@ -1,6 +1,6 @@ """Test form validation requirements.""" -from django.test import TestCase +from django.test import TestCase, RequestFactory from registrar.forms.application_wizard import ( CurrentSitesForm, @@ -16,9 +16,16 @@ from registrar.forms.application_wizard import ( AboutYourOrganizationForm, ) from registrar.forms.domain import ContactForm +from registrar.tests.common import MockEppLib +from django.contrib.auth import get_user_model -class TestFormValidation(TestCase): +class TestFormValidation(MockEppLib): + def setUp(self): + super().setUp() + self.user = get_user_model().objects.create(username="username") + self.factory = RequestFactory() + def test_org_contact_zip_invalid(self): form = OrganizationContactForm(data={"zipcode": "nah"}) self.assertEqual( From dcaaa7099bd8ec1bdb22a18bae46b7e69819974e Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 17 Oct 2023 08:53:04 -0700 Subject: [PATCH 13/32] Delete unknown ! file added --- src/! | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/! diff --git a/src/! b/src/! deleted file mode 100644 index 19765bd50..000000000 --- a/src/! +++ /dev/null @@ -1 +0,0 @@ -null From 981af109dff15247c62144d779f8dd0838544b5e Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 17 Oct 2023 14:00:34 -0700 Subject: [PATCH 14/32] Fix subset of linter errors --- src/api/tests/test_available.py | 50 ++++++++++------------------- src/registrar/tests/common.py | 57 +++++++++++++-------------------- 2 files changed, 40 insertions(+), 67 deletions(-) diff --git a/src/api/tests/test_available.py b/src/api/tests/test_available.py index 3d01228c3..9eab17bf7 100644 --- a/src/api/tests/test_available.py +++ b/src/api/tests/test_available.py @@ -3,24 +3,20 @@ import json from django.contrib.auth import get_user_model -from django.test import TestCase, RequestFactory +from django.test import RequestFactory -from ..views import available, _domains, in_domains +from ..views import available, in_domains from .common import less_console_noise from registrar.tests.common import MockEppLib -from unittest.mock import MagicMock, patch, call +from unittest.mock import call from epplibwrapper import ( commands, - common, - extensions, - responses, RegistryError, - ErrorCode, ) API_BASE_PATH = "/api/v1/available/" -from registrar.models import Domain + class AvailableViewTest(MockEppLib): @@ -34,7 +30,6 @@ class AvailableViewTest(MockEppLib): def test_view_function(self): request = self.factory.get(API_BASE_PATH + "test.gov") request.user = self.user - response = available(request, domain="test.gov") # has the right text in it self.assertContains(response, "available") @@ -42,10 +37,12 @@ class AvailableViewTest(MockEppLib): response_object = json.loads(response.content) self.assertIn("available", response_object) - def test_makes_calls(self): + def test_in_domains_makes_calls_(self): + """Domain searches successfully make correct mock EPP calls""" gsa_available = in_domains("gsa.gov") igorville_available = in_domains("igorvilleremixed.gov") + """Domain searches successfully make mock EPP calls""" self.mockedSendFunction.assert_has_calls( [ call( @@ -59,26 +56,20 @@ class AvailableViewTest(MockEppLib): ["igorvilleremixed.gov"], ), cleaned=True, - ) + ), ] ) - - def test_in_domains(self): - gsa_available = in_domains("gsa.gov") - gsa_caps_available = in_domains("GSA.gov") - igorville_available = in_domains("igorvilleremixed.gov") - + """Domain searches return correct availability results""" self.assertTrue(gsa_available) - # input is lowercased so GSA.GOV should be found - self.assertTrue(gsa_caps_available) - # This domain should not have been registered self.assertFalse(igorville_available) - - def test_in_domains_dotgov(self): - gsa_available = in_domains("gsa.gov") - gsa_caps_available = in_domains("GSA.gov") - igorville_available = in_domains("igorvilleremixed.gov") + def test_in_domains_capitalized(self): + """Domain searches work without case sensitivity""" + self.assertTrue(in_domains("gsa.gov")) + # input is lowercased so GSA.GOV should be found + self.assertTrue(in_domains("GSA.gov")) + + def test_in_domains_dotgov(self): """Domain searches work without trailing .gov""" self.assertTrue(in_domains("gsa")) # input is lowercased so GSA.GOV should be found @@ -86,14 +77,6 @@ class AvailableViewTest(MockEppLib): # This domain should not have been registered self.assertFalse(in_domains("igorvilleremixed")) - def test_in_domains_capitalized(self): - gsa_available = in_domains("gsa.gov") - capitalized_gsa_available = in_domains("GSA.gov") - - """Domain searches work without case sensitivity""" - self.assertTrue(in_domains("gsa.gov")) - self.assertTrue(in_domains("GSA.gov")) - def test_not_available_domain(self): """gsa.gov is not available""" request = self.factory.get(API_BASE_PATH + "gsa.gov") @@ -125,6 +108,7 @@ class AvailableViewTest(MockEppLib): # domain set to raise error successfully raises error with self.assertRaises(RegistryError): error_domain_available = available(request, "errordomain.gov") + self.assertFalse(json.loads(error_domain_available.content)["available"]) class AvailableAPITest(MockEppLib): diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 0144738e2..32117e2db 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -761,6 +761,28 @@ class MockEppLib(TestCase): return MagicMock(res_data=[self.infoDomainThreeHosts]) return MagicMock(res_data=[self.mockDataInfoDomain]) + def _mockDomainName(self, _name, _avail=False): + return MagicMock( + res_data=[ + responses.check.CheckDomainResultData( + name=_name, avail=_avail, reason=None + ), + ] + ) + + def _handleCheckDomain(self, _request): + print(getattr(_request, "names", None)) + if "gsa.gov" in getattr(_request, "names", None): + return self._mockDomainName("gsa.gov", True) + elif "GSA.gov" in getattr(_request, "names", None): + return self._mockDomainName("GSA.gov", True) + elif "igorvilleremixed.gov" in getattr(_request, "names", None): + return self._mockDomainName("igorvilleremixed.gov", False) + elif "errordomain.gov" in getattr(_request, "names", None): + raise RegistryError("Registry cannot find domain availability.") + else: + return self._mockDomainName("domainnotfound.gov", False) + def mockSend(self, _request, cleaned): """Mocks the registry.send function used inside of domain.py registry is imported from epplibwrapper @@ -826,40 +848,7 @@ class MockEppLib(TestCase): code=ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION ) elif isinstance(_request, commands.CheckDomain): - if "gsa.gov" in getattr(_request, "names", None): - return MagicMock( - res_data=[ - responses.check.CheckDomainResultData( - name="gsa.gov", avail=True, reason=None - ), - ] - ) - elif "GSA.gov" in getattr(_request, "names", None): - return MagicMock( - res_data=[ - responses.check.CheckDomainResultData( - name="GSA.gov", avail=True, reason=None - ), - ] - ) - elif "igorvilleremixed.gov" in getattr(_request, "names", None): - return MagicMock( - res_data=[ - responses.check.CheckDomainResultData( - name="igorvilleremixed.gov", avail=False, reason=None - ), - ] - ) - elif "errordomain.gov" in getattr(_request, "names", None): - raise RegistryError("Registry cannot find domain availability.") - else: - return MagicMock( - res_data=[ - responses.check.CheckDomainResultData( - name="domainnotfound.gov", avail=False, reason="In Use" - ) - ], - ) + return self._handleCheckDomain(_request) return MagicMock(res_data=[self.mockDataInfoHosts]) def setUp(self): From 7d6155de1340a0dfba51b87af878200f39502c0f Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 17 Oct 2023 16:06:57 -0700 Subject: [PATCH 15/32] Refactor mockSend to match linter --- src/registrar/tests/common.py | 98 ++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 32117e2db..61444d37f 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -769,9 +769,8 @@ class MockEppLib(TestCase): ), ] ) - + def _handleCheckDomain(self, _request): - print(getattr(_request, "names", None)) if "gsa.gov" in getattr(_request, "names", None): return self._mockDomainName("gsa.gov", True) elif "GSA.gov" in getattr(_request, "names", None): @@ -788,28 +787,8 @@ class MockEppLib(TestCase): registry is imported from epplibwrapper returns objects that simulate what would be in a epp response but only relevant pieces for tests""" - if isinstance(_request, commands.InfoDomain): - return self._getattrInfoDomain(_request) - - elif isinstance(_request, commands.InfoContact): - mocked_result: info.InfoContactResultData - - # For testing contact types - match getattr(_request, "id", None): - case "securityContact": - mocked_result = self.mockSecurityContact - case "technicalContact": - mocked_result = self.mockTechnicalContact - case "adminContact": - mocked_result = self.mockAdministrativeContact - case "regContact": - mocked_result = self.mockRegistrantContact - case _: - # Default contact return - mocked_result = self.mockDataInfoContact - - return MagicMock(res_data=[mocked_result]) - elif ( + print(type(_request) == commands.CheckDomain) + if ( isinstance(_request, commands.CreateContact) and getattr(_request, "id", None) == "fail" and self.mockedSendFunction.call_count == 3 @@ -817,27 +796,8 @@ class MockEppLib(TestCase): # use this for when a contact is being updated # sets the second send() to fail raise RegistryError(code=ErrorCode.OBJECT_EXISTS) - elif isinstance(_request, commands.CreateHost): - return MagicMock( - res_data=[self.mockDataHostChange], - code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, - ) - elif isinstance(_request, commands.UpdateHost): - return MagicMock( - res_data=[self.mockDataHostChange], - code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, - ) - elif isinstance(_request, commands.UpdateDomain): - return MagicMock( - res_data=[self.mockDataHostChange], - code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, - ) - elif isinstance(_request, commands.DeleteHost): - return MagicMock( - res_data=[self.mockDataHostChange], - code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, - ) - elif ( + + if ( isinstance(_request, commands.DeleteDomain) and getattr(_request, "name", None) == "failDelete.gov" ): @@ -847,9 +807,51 @@ class MockEppLib(TestCase): raise RegistryError( code=ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION ) - elif isinstance(_request, commands.CheckDomain): - return self._handleCheckDomain(_request) - return MagicMock(res_data=[self.mockDataInfoHosts]) + + match type(_request): + case commands.InfoDomain: + return self._getattrInfoDomain(_request) + case commands.InfoContact: + mocked_result: info.InfoContactResultData + + # For testing contact types + match getattr(_request, "id", None): + case "securityContact": + mocked_result = self.mockSecurityContact + case "technicalContact": + mocked_result = self.mockTechnicalContact + case "adminContact": + mocked_result = self.mockAdministrativeContact + case "regContact": + mocked_result = self.mockRegistrantContact + case _: + # Default contact return + mocked_result = self.mockDataInfoContact + return MagicMock(res_data=[mocked_result]) + case commands.CreateHost: + return MagicMock( + res_data=[self.mockDataHostChange], + code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, + ) + case commands.UpdateHost: + return MagicMock( + res_data=[self.mockDataHostChange], + code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, + ) + case commands.UpdateDomain: + return MagicMock( + res_data=[self.mockDataHostChange], + code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, + ) + case commands.DeleteHost: + return MagicMock( + res_data=[self.mockDataHostChange], + code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, + ) + case commands.CheckDomain: + return self._handleCheckDomain(_request) + case _: + return MagicMock(res_data=[self.mockDataInfoHosts]) def setUp(self): """mock epp send function as this will fail locally""" From 1791db42035c19a85bef66bed4a99fdcc0f99a52 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Wed, 18 Oct 2023 18:57:58 -0400 Subject: [PATCH 16/32] add more specificity to CSS selectors causing a btn color bug --- src/registrar/assets/sass/_theme/_buttons.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/registrar/assets/sass/_theme/_buttons.scss b/src/registrar/assets/sass/_theme/_buttons.scss index 718bd5792..0857ec603 100644 --- a/src/registrar/assets/sass/_theme/_buttons.scss +++ b/src/registrar/assets/sass/_theme/_buttons.scss @@ -22,15 +22,15 @@ a.breadcrumb__back { } } -a.usa-button { +a.usa-button:not(.usa-button--unstyled, .usa-button--outline) { text-decoration: none; color: color('white'); } -a.usa-button:visited, -a.usa-button:hover, -a.usa-button:focus, -a.usa-button:active { +a.usa-button:not(.usa-button--unstyled, .usa-button--outline):visited, +a.usa-button:not(.usa-button--unstyled, .usa-button--outline):hover, +a.usa-button:not(.usa-button--unstyled, .usa-button--outline):focus, +a.usa-button:not(.usa-button--unstyled, .usa-button--outline):active { color: color('white'); } From eebe6e117efb9f2ef9e5cdf1fe81e9882a45959c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 19 Oct 2023 10:50:18 -0600 Subject: [PATCH 17/32] Fix merge issue --- src/registrar/tests/common.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 94e3e2d3e..f6c6c1f2d 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -900,11 +900,10 @@ class MockEppLib(TestCase): "namerserversubdomain.gov": (self.infoDomainCheckHostIPCombo, None), "freeman.gov": (self.InfoDomainWithContacts, None), "threenameserversDomain.gov": (self.infoDomainThreeHosts, None), + "defaultsecurity.gov": (self.InfoDomainWithDefaultSecurityContact, None), + "defaulttechnical.gov": (self.InfoDomainWithDefaultTechnicalContact, None) } - TODO = elif getattr(_request, "name", None) == "defaultsecurity.gov": - return MagicMock(res_data=[self.InfoDomainWithDefaultSecurityContact]) - elif getattr(_request, "name", None) == "defaulttechnical.gov": - return MagicMock(res_data=[self.InfoDomainWithDefaultTechnicalContact]) + # Retrieve the corresponding values from the dictionary res_data, extensions = request_mappings.get( request_name, (self.mockDataInfoDomain, None) From e26db14a29b441d632f083ffcbc0acb62233c186 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 19 Oct 2023 10:53:54 -0600 Subject: [PATCH 18/32] Fix merge weirdness --- src/registrar/tests/common.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index f6c6c1f2d..acda90a75 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -917,23 +917,23 @@ class MockEppLib(TestCase): def mockInfoContactCommands(self, _request, cleaned): mocked_result: info.InfoContactResultData - # For testing contact types - match getattr(_request, "id", None): - case "securityContact": - mocked_result = self.mockSecurityContact - case "technicalContact": - mocked_result = self.mockTechnicalContact - case "adminContact": - mocked_result = self.mockAdministrativeContact - case "regContact": - mocked_result = self.mockRegistrantContact - case "defaultSec": - mocked_result = self.mockDefaultSecurityContact - case "defaultTech": - mocked_result = self.mockDefaultTechnicalContact - case _: - # Default contact return - mocked_result = self.mockDataInfoContact + # For testing contact types + match getattr(_request, "id", None): + case "securityContact": + mocked_result = self.mockSecurityContact + case "technicalContact": + mocked_result = self.mockTechnicalContact + case "adminContact": + mocked_result = self.mockAdministrativeContact + case "regContact": + mocked_result = self.mockRegistrantContact + case "defaultSec": + mocked_result = self.mockDefaultSecurityContact + case "defaultTech": + mocked_result = self.mockDefaultTechnicalContact + case _: + # Default contact return + mocked_result = self.mockDataInfoContact return MagicMock(res_data=[mocked_result]) From afd7b1c3f332f619677fa13fc1fd23fb1bc9f4e5 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 19 Oct 2023 11:51:52 -0600 Subject: [PATCH 19/32] Reformat + add test case for eppdisclose --- src/registrar/tests/common.py | 2 +- src/registrar/tests/test_models_domain.py | 69 ++++++++++++++++++++++- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index acda90a75..bd6a6336b 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -901,7 +901,7 @@ class MockEppLib(TestCase): "freeman.gov": (self.InfoDomainWithContacts, None), "threenameserversDomain.gov": (self.infoDomainThreeHosts, None), "defaultsecurity.gov": (self.InfoDomainWithDefaultSecurityContact, None), - "defaulttechnical.gov": (self.InfoDomainWithDefaultTechnicalContact, None) + "defaulttechnical.gov": (self.InfoDomainWithDefaultTechnicalContact, None), } # Retrieve the corresponding values from the dictionary diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 7a66c1106..76f8f1d2a 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -19,7 +19,7 @@ from registrar.utility.errors import ActionNotAllowed, NameserverError from registrar.models.utility.contact_error import ContactError, ContactErrorCodes -from .common import MockEppLib + from django_fsm import TransitionNotAllowed # type: ignore from epplibwrapper import ( commands, @@ -29,6 +29,7 @@ from epplibwrapper import ( RegistryError, ErrorCode, ) +from .common import MockEppLib import logging logger = logging.getLogger(__name__) @@ -813,6 +814,72 @@ class TestRegistrantContacts(MockEppLib): # The emails should match on both items self.assertEqual(expected_contact.email, actual_contact.email) + def test_convert_public_contact_to_epp(self): + self.maxDiff = None + domain, _ = Domain.objects.get_or_create(name="freeman.gov") + dummy_contact = domain.get_default_security_contact() + test_disclose = self._convertPublicContactToEpp( + dummy_contact, disclose_email=True + ).__dict__ + test_not_disclose = self._convertPublicContactToEpp( + dummy_contact, disclose_email=False + ).__dict__ + expected_disclose = { + "auth_info": common.ContactAuthInfo(pw='2fooBAR123fooBaz'), + "disclose": common.Disclose(flag=True, fields={common.DiscloseField.EMAIL}, types=None), + "email": "dotgov@cisa.dhs.gov", + "extensions": [], + "fax": None, + "id": "ThIq2NcRIDN7PauO", + "ident": None, + "notify_email": None, + "postal_info": common.PostalInfo( + name='Registry Customer Service', + addr=common.ContactAddr( + street=['4200 Wilson Blvd.', None, None], + city='Arlington', + pc='22201', + cc='US', + sp='VA' + ), + org='Cybersecurity and Infrastructure Security Agency', + type='loc' + ), + "vat": None, + "voice": "+1.8882820870" + } + expected_not_disclose = { + "auth_info": common.ContactAuthInfo(pw='2fooBAR123fooBaz'), + "disclose": common.Disclose(flag=False, fields={common.DiscloseField.EMAIL}, types=None), + "email": "dotgov@cisa.dhs.gov", + "extensions": [], + "fax": None, + "id": "ThrECENCHI76PGLh", + "ident": None, + "notify_email": None, + "postal_info": common.PostalInfo( + name='Registry Customer Service', + addr=common.ContactAddr( + street=['4200 Wilson Blvd.', None, None], + city='Arlington', + pc='22201', + cc='US', + sp='VA' + ), + org='Cybersecurity and Infrastructure Security Agency', + type='loc' + ), + "vat": None, + "voice": "+1.8882820870" + } + + # Set the ids equal, since this value changes + test_disclose["id"] = expected_disclose["id"] + test_not_disclose["id"] = expected_not_disclose["id"] + + self.assertEqual(test_disclose, expected_disclose) + self.assertEqual(test_not_disclose, expected_not_disclose) + def test_not_disclosed_on_default_security_contact(self): """ Scenario: Registrant creates a new domain with no security email From 81b58167a61b7ceee66230af314f73dba3da0be1 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Thu, 19 Oct 2023 11:13:55 -0700 Subject: [PATCH 20/32] Fix lint errors --- src/registrar/tests/common.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index c61b827fc..f6539466d 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -851,7 +851,7 @@ class MockEppLib(TestCase): res_data=[self.mockDataHostChange], code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, ) - + def mockDeleteDomainCommands(self, _request, cleaned): if getattr(_request, "name", None) == "failDelete.gov": name = getattr(_request, "name", None) @@ -862,7 +862,6 @@ class MockEppLib(TestCase): ) return None - def mockInfoDomainCommands(self, _request, cleaned): request_name = getattr(_request, "name", None) From 15ecaa83e70f0c40f8a5a06dc0f506a54967cbdc Mon Sep 17 00:00:00 2001 From: Cameron Dixon Date: Fri, 20 Oct 2023 06:33:11 -0400 Subject: [PATCH 21/32] Separate additional context from the description --- .github/ISSUE_TEMPLATE/issue-default.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/issue-default.yml b/.github/ISSUE_TEMPLATE/issue-default.yml index 2252845bf..701742f72 100644 --- a/.github/ISSUE_TEMPLATE/issue-default.yml +++ b/.github/ISSUE_TEMPLATE/issue-default.yml @@ -10,9 +10,9 @@ body: - type: textarea id: issue-description attributes: - label: Issue description and context + label: Issue description description: | - Describe the issue so that someone who wasn't present for its discovery can understand why it matters. Use full sentences, plain language, and good [formatting](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax). Screenshots and links to documents/discussions are welcome. + Describe the issue so that someone who wasn't present for its discovery can understand why it matters. Use full sentences, plain language, and good [formatting](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax). validations: required: true - type: textarea @@ -21,6 +21,11 @@ body: label: Acceptance criteria description: "If known, share 1-3 statements that would need to be true for this issue to be considered resolved. Use a [task list](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/about-task-lists#creating-task-lists) if appropriate." placeholder: "- [ ]" + - type: textarea + id: additional-context + attributes: + label: Additional context + description: "Share any other thoughts, like how this might be implemented or fixed. Screenshots and links to documents/discussions are welcome." - type: textarea id: links-to-other-issues attributes: @@ -32,4 +37,5 @@ body: id: note attributes: value: | - > We may edit this issue's text to document our understanding and clarify the product work. + > We may edit the text in this issue to document our understanding and clarify the product work. + From 0b087a81517c4e2a66e14844de18adeb2441ad41 Mon Sep 17 00:00:00 2001 From: Cameron Dixon Date: Fri, 20 Oct 2023 06:38:34 -0400 Subject: [PATCH 22/32] fix spacing --- .github/ISSUE_TEMPLATE/issue-default.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/issue-default.yml b/.github/ISSUE_TEMPLATE/issue-default.yml index 701742f72..26384ceda 100644 --- a/.github/ISSUE_TEMPLATE/issue-default.yml +++ b/.github/ISSUE_TEMPLATE/issue-default.yml @@ -24,8 +24,8 @@ body: - type: textarea id: additional-context attributes: - label: Additional context - description: "Share any other thoughts, like how this might be implemented or fixed. Screenshots and links to documents/discussions are welcome." + label: Additional context + description: "Share any other thoughts, like how this might be implemented or fixed. Screenshots and links to documents/discussions are welcome." - type: textarea id: links-to-other-issues attributes: From 94a8bf7793c3b5b4ffccde707027488c9ee430c2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 20 Oct 2023 07:56:00 -0600 Subject: [PATCH 23/32] Linting --- src/registrar/tests/test_models_domain.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 76f8f1d2a..939d765b6 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -824,9 +824,13 @@ class TestRegistrantContacts(MockEppLib): test_not_disclose = self._convertPublicContactToEpp( dummy_contact, disclose_email=False ).__dict__ + + # Separated for linter + disclose_email_field = {common.DiscloseField.EMAIL} + disclose = common.Disclose(flag=True, fields=disclose_email_field, types=None), expected_disclose = { "auth_info": common.ContactAuthInfo(pw='2fooBAR123fooBaz'), - "disclose": common.Disclose(flag=True, fields={common.DiscloseField.EMAIL}, types=None), + "disclose": disclose, "email": "dotgov@cisa.dhs.gov", "extensions": [], "fax": None, @@ -848,9 +852,12 @@ class TestRegistrantContacts(MockEppLib): "vat": None, "voice": "+1.8882820870" } + + # Separated for linter + not_disclose = common.Disclose(flag=False, fields=disclose_email_field, types=None) expected_not_disclose = { "auth_info": common.ContactAuthInfo(pw='2fooBAR123fooBaz'), - "disclose": common.Disclose(flag=False, fields={common.DiscloseField.EMAIL}, types=None), + "disclose": not_disclose, "email": "dotgov@cisa.dhs.gov", "extensions": [], "fax": None, From d5b47a22df78eaa0e8ec22620ab68eae933abcf3 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 20 Oct 2023 08:04:21 -0600 Subject: [PATCH 24/32] Update test_models_domain.py --- src/registrar/tests/test_models_domain.py | 50 ++++++++++++----------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 939d765b6..5759df1be 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -827,10 +827,11 @@ class TestRegistrantContacts(MockEppLib): # Separated for linter disclose_email_field = {common.DiscloseField.EMAIL} - disclose = common.Disclose(flag=True, fields=disclose_email_field, types=None), expected_disclose = { - "auth_info": common.ContactAuthInfo(pw='2fooBAR123fooBaz'), - "disclose": disclose, + "auth_info": common.ContactAuthInfo(pw="2fooBAR123fooBaz"), + "disclose": common.Disclose( + flag=True, fields=disclose_email_field, types=None + ), "email": "dotgov@cisa.dhs.gov", "extensions": [], "fax": None, @@ -838,26 +839,27 @@ class TestRegistrantContacts(MockEppLib): "ident": None, "notify_email": None, "postal_info": common.PostalInfo( - name='Registry Customer Service', + name="Registry Customer Service", addr=common.ContactAddr( - street=['4200 Wilson Blvd.', None, None], - city='Arlington', - pc='22201', - cc='US', - sp='VA' + street=["4200 Wilson Blvd.", None, None], + city="Arlington", + pc="22201", + cc="US", + sp="VA", ), - org='Cybersecurity and Infrastructure Security Agency', - type='loc' + org="Cybersecurity and Infrastructure Security Agency", + type="loc", ), "vat": None, - "voice": "+1.8882820870" + "voice": "+1.8882820870", } # Separated for linter - not_disclose = common.Disclose(flag=False, fields=disclose_email_field, types=None) expected_not_disclose = { - "auth_info": common.ContactAuthInfo(pw='2fooBAR123fooBaz'), - "disclose": not_disclose, + "auth_info": common.ContactAuthInfo(pw="2fooBAR123fooBaz"), + "disclose": common.Disclose( + flag=False, fields=disclose_email_field, types=None + ), "email": "dotgov@cisa.dhs.gov", "extensions": [], "fax": None, @@ -865,19 +867,19 @@ class TestRegistrantContacts(MockEppLib): "ident": None, "notify_email": None, "postal_info": common.PostalInfo( - name='Registry Customer Service', + name="Registry Customer Service", addr=common.ContactAddr( - street=['4200 Wilson Blvd.', None, None], - city='Arlington', - pc='22201', - cc='US', - sp='VA' + street=["4200 Wilson Blvd.", None, None], + city="Arlington", + pc="22201", + cc="US", + sp="VA", ), - org='Cybersecurity and Infrastructure Security Agency', - type='loc' + org="Cybersecurity and Infrastructure Security Agency", + type="loc", ), "vat": None, - "voice": "+1.8882820870" + "voice": "+1.8882820870", } # Set the ids equal, since this value changes From 485a05e45e133b508cdd0cf64d18078eb9d0e828 Mon Sep 17 00:00:00 2001 From: Alysia Broddrick Date: Fri, 20 Oct 2023 08:44:12 -0700 Subject: [PATCH 25/32] Fixed manager role still showing as admin --- .../0040_alter_userdomainrole_role.py | 17 +++++++++++++++++ src/registrar/models/user_domain_role.py | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 src/registrar/migrations/0040_alter_userdomainrole_role.py diff --git a/src/registrar/migrations/0040_alter_userdomainrole_role.py b/src/registrar/migrations/0040_alter_userdomainrole_role.py new file mode 100644 index 000000000..39e539f55 --- /dev/null +++ b/src/registrar/migrations/0040_alter_userdomainrole_role.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.1 on 2023-10-20 15:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0039_alter_transitiondomain_status"), + ] + + operations = [ + migrations.AlterField( + model_name="userdomainrole", + name="role", + field=models.TextField(choices=[("manager", "Manager")]), + ), + ] diff --git a/src/registrar/models/user_domain_role.py b/src/registrar/models/user_domain_role.py index e5cb01cc1..7b1f550d3 100644 --- a/src/registrar/models/user_domain_role.py +++ b/src/registrar/models/user_domain_role.py @@ -15,7 +15,7 @@ class UserDomainRole(TimeStampedModel): elsewhere. """ - ADMIN = "manager" + MANAGER = "manager" user = models.ForeignKey( "registrar.User", From 649a22456232f77f75f98bd6ecedc09d3ac62529 Mon Sep 17 00:00:00 2001 From: Alysia Broddrick Date: Fri, 20 Oct 2023 09:15:46 -0700 Subject: [PATCH 26/32] updated related mentions to admin to be Manager --- docs/developer/user-permissions.md | 2 +- src/registrar/models/domain_application.py | 2 +- src/registrar/models/domain_invitation.py | 2 +- src/registrar/tests/test_models.py | 2 +- src/registrar/tests/test_views.py | 12 ++++++------ src/registrar/views/domain.py | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/developer/user-permissions.md b/docs/developer/user-permissions.md index 31b69d3b3..f7c41492d 100644 --- a/docs/developer/user-permissions.md +++ b/docs/developer/user-permissions.md @@ -42,7 +42,7 @@ as health checks used by our platform). ## Adding roles The current MVP design uses only a single role called -`UserDomainRole.Roles.ADMIN` that has all access on a domain. As such, the +`UserDomainRole.Roles.MANAGER` that has all access on a domain. As such, the permission mixin doesn't need to examine the `role` field carefully. In the future, as we add additional roles that our product vision calls for (read-only? editing only some information?), we need to add conditional diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 68429d381..f15474117 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -582,7 +582,7 @@ class DomainApplication(TimeStampedModel): # create the permission for the user UserDomainRole = apps.get_model("registrar.UserDomainRole") UserDomainRole.objects.get_or_create( - user=self.creator, domain=created_domain, role=UserDomainRole.Roles.ADMIN + user=self.creator, domain=created_domain, role=UserDomainRole.Roles.MANAGER ) self._send_status_update_email( diff --git a/src/registrar/models/domain_invitation.py b/src/registrar/models/domain_invitation.py index 7cc2a5432..dff03fb87 100644 --- a/src/registrar/models/domain_invitation.py +++ b/src/registrar/models/domain_invitation.py @@ -63,7 +63,7 @@ class DomainInvitation(TimeStampedModel): # and create a role for that user on this domain _, created = UserDomainRole.objects.get_or_create( - user=user, domain=self.domain, role=UserDomainRole.Roles.ADMIN + user=user, domain=self.domain, role=UserDomainRole.Roles.MANAGER ) if not created: # something strange happened and this role already existed when diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index 2c6f78ef5..e76dea035 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -601,7 +601,7 @@ class TestInvitations(TestCase): def test_retrieve_existing_role_no_error(self): # make the overlapping role UserDomainRole.objects.get_or_create( - user=self.user, domain=self.domain, role=UserDomainRole.Roles.ADMIN + user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER ) # this is not an error but does produce a console warning with less_console_noise(): diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 0e8f895af..2a14f3466 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -89,7 +89,7 @@ class LoggedInTests(TestWithUser): domain, _ = Domain.objects.get_or_create(name="igorville.gov") self.assertNotContains(response, "igorville.gov") role, _ = UserDomainRole.objects.get_or_create( - user=self.user, domain=domain, role=UserDomainRole.Roles.ADMIN + user=self.user, domain=domain, role=UserDomainRole.Roles.MANAGER ) response = self.client.get("/") # count = 2 because it is also in screenreader content @@ -1097,23 +1097,23 @@ class TestWithDomainPermissions(TestWithUser): creator=self.user, domain=self.domain_dnssec_none ) self.role, _ = UserDomainRole.objects.get_or_create( - user=self.user, domain=self.domain, role=UserDomainRole.Roles.ADMIN + user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER ) UserDomainRole.objects.get_or_create( - user=self.user, domain=self.domain_dsdata, role=UserDomainRole.Roles.ADMIN + user=self.user, domain=self.domain_dsdata, role=UserDomainRole.Roles.MANAGER ) UserDomainRole.objects.get_or_create( user=self.user, domain=self.domain_multdsdata, - role=UserDomainRole.Roles.ADMIN, + role=UserDomainRole.Roles.MANAGER, ) UserDomainRole.objects.get_or_create( - user=self.user, domain=self.domain_keydata, role=UserDomainRole.Roles.ADMIN + user=self.user, domain=self.domain_keydata, role=UserDomainRole.Roles.MANAGER ) UserDomainRole.objects.get_or_create( user=self.user, domain=self.domain_dnssec_none, - role=UserDomainRole.Roles.ADMIN, + role=UserDomainRole.Roles.MANAGER, ) def tearDown(self): diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index aa71a7551..5590698be 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -736,7 +736,7 @@ class DomainAddUserView(DomainFormBaseView): try: UserDomainRole.objects.create( - user=requested_user, domain=self.object, role=UserDomainRole.Roles.ADMIN + user=requested_user, domain=self.object, role=UserDomainRole.Roles.MANAGER ) except IntegrityError: # User already has the desired role! Do nothing?? From 555a1e7ae95a8e8ea6f3debce1ab65c94ca34c29 Mon Sep 17 00:00:00 2001 From: Alysia Broddrick Date: Fri, 20 Oct 2023 09:21:12 -0700 Subject: [PATCH 27/32] ran linter --- src/registrar/tests/test_views.py | 4 +++- src/registrar/views/domain.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 2a14f3466..7cc616889 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -1108,7 +1108,9 @@ class TestWithDomainPermissions(TestWithUser): role=UserDomainRole.Roles.MANAGER, ) UserDomainRole.objects.get_or_create( - user=self.user, domain=self.domain_keydata, role=UserDomainRole.Roles.MANAGER + user=self.user, + domain=self.domain_keydata, + role=UserDomainRole.Roles.MANAGER, ) UserDomainRole.objects.get_or_create( user=self.user, diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index 5590698be..fce94f175 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -736,7 +736,9 @@ class DomainAddUserView(DomainFormBaseView): try: UserDomainRole.objects.create( - user=requested_user, domain=self.object, role=UserDomainRole.Roles.MANAGER + user=requested_user, + domain=self.object, + role=UserDomainRole.Roles.MANAGER, ) except IntegrityError: # User already has the desired role! Do nothing?? From 8c0d88df2ac1a97b3f889b4437fa41904224c578 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Fri, 20 Oct 2023 09:35:28 -0700 Subject: [PATCH 28/32] Remove login requirement on domain availability API --- src/api/views.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/api/views.py b/src/api/views.py index 02e419a91..e8b8431de 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -3,8 +3,6 @@ from django.apps import apps from django.views.decorators.http import require_http_methods from django.http import JsonResponse -from django.contrib.auth.decorators import login_required - import requests from cachetools.func import ttl_cache @@ -68,7 +66,6 @@ def in_domains(domain): @require_http_methods(["GET"]) -@login_required def available(request, domain=""): """Is a given domain available or not. From 3cfdacccfc3bc5aaf7aa71eb97da959b2cf1fc44 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Fri, 20 Oct 2023 12:00:17 -0500 Subject: [PATCH 29/32] Change User management to Domain managers --- src/registrar/templates/domain_detail.html | 2 +- src/registrar/templates/domain_sidebar.html | 2 +- src/registrar/templates/domain_users.html | 17 +++++++++++++++-- src/registrar/tests/test_views.py | 2 +- src/registrar/views/domain.py | 2 +- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/registrar/templates/domain_detail.html b/src/registrar/templates/domain_detail.html index e0d672093..4ddbd673a 100644 --- a/src/registrar/templates/domain_detail.html +++ b/src/registrar/templates/domain_detail.html @@ -52,7 +52,7 @@ {% include "includes/summary_item.html" with title='Security email' value='None provided' edit_link=url %} {% endif %} {% url 'domain-users' pk=domain.id as url %} - {% include "includes/summary_item.html" with title='User management' users='true' list=True value=domain.permissions.all edit_link=url %} + {% include "includes/summary_item.html" with title='Domain managers' users='true' list=True value=domain.permissions.all edit_link=url %} {% endblock %} {# domain_content #} diff --git a/src/registrar/templates/domain_sidebar.html b/src/registrar/templates/domain_sidebar.html index 1acd87eeb..ac45ad04c 100644 --- a/src/registrar/templates/domain_sidebar.html +++ b/src/registrar/templates/domain_sidebar.html @@ -100,7 +100,7 @@ - User management + Domain managers diff --git a/src/registrar/templates/domain_users.html b/src/registrar/templates/domain_users.html index 22b9d18d1..5cb7acffd 100644 --- a/src/registrar/templates/domain_users.html +++ b/src/registrar/templates/domain_users.html @@ -1,10 +1,23 @@ {% extends "domain_base.html" %} {% load static %} -{% block title %}User management | {{ domain.name }} | {% endblock %} +{% block title %}Domain managers | {{ domain.name }} | {% endblock %} {% block domain_content %} -

User management

+

Domain managers

+ +

+ Domain managers can update all information related to a domain within the + .gov registrar, including contact details, authorizing official, security + email, and DNS name servers. +

+ +
    +
  • There is no limit to the number of domain managers you can add.
  • +
  • After adding a domain manager, an email invitation will be sent to that user with + instructions on how to set up an account.
  • +
  • To remove a domain manager, contact us for assistance. +
{% if domain.permissions %}
diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 0e8f895af..8ad855433 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -1204,7 +1204,7 @@ class TestDomainUserManagement(TestDomainOverview): response = self.client.get( reverse("domain-users", kwargs={"pk": self.domain.id}) ) - self.assertContains(response, "User management") + self.assertContains(response, "Domain managers") def test_domain_user_management_add_link(self): """Button to get to user add page works.""" diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index aa71a7551..d9b671a65 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -656,7 +656,7 @@ class DomainSecurityEmailView(DomainFormBaseView): class DomainUsersView(DomainBaseView): - """User management page in the domain details.""" + """Domain managers page in the domain details.""" template_name = "domain_users.html" From a0911b46f84e7b16df9c0d38de591799a99c91e0 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Fri, 20 Oct 2023 13:28:36 -0500 Subject: [PATCH 30/32] load public_site_url helper --- src/registrar/templates/domain_users.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/domain_users.html b/src/registrar/templates/domain_users.html index 5cb7acffd..f66eef5a6 100644 --- a/src/registrar/templates/domain_users.html +++ b/src/registrar/templates/domain_users.html @@ -1,5 +1,5 @@ {% extends "domain_base.html" %} -{% load static %} +{% load static url_helpers %} {% block title %}Domain managers | {{ domain.name }} | {% endblock %} From 7f153f77ed70147c39e021ab02d85498c1cb4bf2 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Fri, 20 Oct 2023 13:46:51 -0500 Subject: [PATCH 31/32] Review feedback: rename tests to match page name --- src/registrar/tests/test_views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 8ad855433..1262347a1 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -1199,14 +1199,14 @@ class TestDomainOverview(TestWithDomainPermissions, WebTest): self.assertEqual(response.status_code, 403) -class TestDomainUserManagement(TestDomainOverview): - def test_domain_user_management(self): +class TestDomainManagers(TestDomainOverview): + def test_domain_managers(self): response = self.client.get( reverse("domain-users", kwargs={"pk": self.domain.id}) ) self.assertContains(response, "Domain managers") - def test_domain_user_management_add_link(self): + def test_domain_managers_add_link(self): """Button to get to user add page works.""" management_page = self.app.get( reverse("domain-users", kwargs={"pk": self.domain.id}) From 020f2614d0e7c727d0261a4f9bf3da0c22fbff3e Mon Sep 17 00:00:00 2001 From: Cameron Dixon Date: Fri, 20 Oct 2023 15:46:51 -0400 Subject: [PATCH 32/32] Update issue-default.yml --- .github/ISSUE_TEMPLATE/issue-default.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/issue-default.yml b/.github/ISSUE_TEMPLATE/issue-default.yml index 26384ceda..3a34b2943 100644 --- a/.github/ISSUE_TEMPLATE/issue-default.yml +++ b/.github/ISSUE_TEMPLATE/issue-default.yml @@ -21,9 +21,9 @@ body: label: Acceptance criteria description: "If known, share 1-3 statements that would need to be true for this issue to be considered resolved. Use a [task list](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/about-task-lists#creating-task-lists) if appropriate." placeholder: "- [ ]" - - type: textarea - id: additional-context - attributes: + - type: textarea + id: additional-context + attributes: label: Additional context description: "Share any other thoughts, like how this might be implemented or fixed. Screenshots and links to documents/discussions are welcome." - type: textarea