diff --git a/src/registrar/management/commands/disclose_security_emails.py b/src/registrar/management/commands/disclose_security_emails.py new file mode 100644 index 000000000..62989e4c0 --- /dev/null +++ b/src/registrar/management/commands/disclose_security_emails.py @@ -0,0 +1,69 @@ +"""" +Converts all ready and DNS needed domains with a non-default public contact +to disclose their public contact. Created for Issue#1535 to resolve + disclose issue of domains with missing security emails. +""" + +import logging +import copy + +from django.core.management import BaseCommand +from registrar.models import Domain + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + help = "Disclose all nondefault domain security emails." + + def __init__(self): + """Sets global variables for code tidiness""" + super().__init__() + # domains with errors, which are not successfully updated to disclose + self.domains_with_errors: list[str] = [] + # domains that are successfully disclosed + self.disclosed_domain_contacts_count = 0 + # domains that skip disclose due to having contact registrar@dotgov.gov + self.skipped_domain_contacts_count = 0 + + def handle(self, **options): + """ + Converts all ready and DNS needed domains with a non-default public contact + to disclose their public contact. + """ + logger.info("Updating security emails to public") + + # Initializes domains that need to be disclosed + + statuses = [Domain.State.READY, Domain.State.DNS_NEEDED] + domains = Domain.objects.filter(state__in=statuses) + + logger.info(f"Found {len(domains)} domains with status Ready or DNS Needed.") + + # Update EPP contact for domains with a security contact + for domain in domains: + try: + contact = domain.security_contact # noqa on these items as we only want to call security_contact + logger.info(f"Domain {domain.name} security contact: {domain.security_contact.email}") + if domain.security_contact.email != "registrar@dotgov.gov": + domain._update_epp_contact(contact=domain.security_contact) + self.disclosed_domain_contacts_count += 1 + else: + logger.info( + f"Skipping disclose for {domain.name} security contact {domain.security_contact.email}." + ) + self.skipped_domain_contacts_count += 1 + except Exception as err: + # error condition if domain not in database + self.domains_with_errors.append(copy.deepcopy(domain.name)) + logger.error(f"error retrieving domain {domain.name} contact {domain.security_contact}: {err}") + + # Inform user how many contacts were disclosed, skipped, and errored + logger.info(f"Updated {self.disclosed_domain_contacts_count} contacts to disclosed.") + logger.info( + f"Skipped disclosing {self.skipped_domain_contacts_count} contacts with security email " + f"registrar@dotgov.gov." + ) + logger.info( + f"Error disclosing the following {len(self.domains_with_errors)} contacts: {self.domains_with_errors}" + ) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 001937b89..1a581a4ec 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -1396,11 +1396,13 @@ class Domain(TimeStampedModel, DomainHelper): def _disclose_fields(self, contact: PublicContact): """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""" + if item is security email then make sure email is visible""" is_security = contact.contact_type == contact.ContactTypeChoices.SECURITY DF = epp.DiscloseField fields = {DF.EMAIL} disclose = is_security and contact.email != PublicContact.get_default_security().email + # Delete after testing on other devices + logger.info("Updated domain contact %s to disclose: %s", contact.email, disclose) # Will only disclose DF.EMAIL if its not the default return epp.Disclose( flag=disclose, diff --git a/src/registrar/tests/test_management_scripts.py b/src/registrar/tests/test_management_scripts.py index e557eed45..06886ba66 100644 --- a/src/registrar/tests/test_management_scripts.py +++ b/src/registrar/tests/test_management_scripts.py @@ -11,9 +11,11 @@ from registrar.models import ( DomainInformation, UserDomainRole, ) +from registrar.models.public_contact import PublicContact from django.core.management import call_command -from unittest.mock import patch +from unittest.mock import patch, call +from epplibwrapper import commands, common from .common import MockEppLib @@ -441,3 +443,57 @@ class TestExtendExpirationDates(MockEppLib): # Explicitly test the expiration date - should be the same self.assertEqual(desired_domain.expiration_date, datetime.date(2024, 11, 15)) + + +class TestDiscloseEmails(MockEppLib): + def setUp(self): + super().setUp() + + def tearDown(self): + super().tearDown() + PublicContact.objects.all().delete() + Domain.objects.all().delete() + + def run_disclose_security_emails(self): + """ + This method executes the disclose_security_emails command. + + The 'call_command' function from Django's management framework is then used to + execute the disclose_security_emails command. + """ + with patch( + "registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa + return_value=True, + ): + call_command("disclose_security_emails") + + def test_disclose_security_emails(self): + """ + Tests that command disclose_security_emails runs successfully with + appropriate EPP calll to UpdateContact. + """ + domain, _ = Domain.objects.get_or_create(name="testdisclose.gov", state=Domain.State.READY) + expectedSecContact = PublicContact.get_default_security() + expectedSecContact.domain = domain + expectedSecContact.email = "123@mail.gov" + # set domain security email to 123@mail.gov instead of default email + domain.security_contact = expectedSecContact + self.run_disclose_security_emails() + + # running disclose_security_emails sends EPP call UpdateContact with disclose + self.mockedSendFunction.assert_has_calls( + [ + call( + commands.UpdateContact( + id=domain.security_contact.registry_id, + postal_info=domain._make_epp_contact_postal_info(contact=domain.security_contact), + email=domain.security_contact.email, + voice=domain.security_contact.voice, + fax=domain.security_contact.fax, + auth_info=common.ContactAuthInfo(pw="2fooBAR123fooBaz"), + disclose=domain._disclose_fields(contact=domain.security_contact), + ), + cleaned=True, + ) + ] + )