diff --git a/docs/operations/runbooks/rotate_application_secrets.md b/docs/operations/runbooks/rotate_application_secrets.md index a776e60b8..f7a5004ef 100644 --- a/docs/operations/runbooks/rotate_application_secrets.md +++ b/docs/operations/runbooks/rotate_application_secrets.md @@ -117,3 +117,9 @@ You'll need to give the new certificate to the registry vendor _before_ rotating ## REGISTRY_HOSTNAME This is the hostname at which the registry can be found. + +## SECRET_METADATA_KEY + +This is in reference to the key for the metadata email that is sent daily. Reach out to product team members or leads with access to security passwords if the passcode is needed. + +To change the password, use a password generator to generate a password, then update the user credentials per the above instructions. Be sure to update the `KDBX` file in Google Drive with this password change. diff --git a/src/registrar/management/commands/generate_current_metadata_report.py b/src/registrar/management/commands/generate_current_metadata_report.py index 023a19f10..2478f9e6b 100644 --- a/src/registrar/management/commands/generate_current_metadata_report.py +++ b/src/registrar/management/commands/generate_current_metadata_report.py @@ -72,7 +72,7 @@ class Command(BaseCommand): # Set context for the subject current_date_str = datetime.now().strftime("%Y-%m-%d") - # TODO: Update secret in getgov-credentials via cloud.gov and my own .env when ready + # TODO: Update secret in getgov-credentials via cloud.gov and my own .env when merging # Encrypt the metadata encrypted_metadata_in_bytes = self._encrypt_metadata( @@ -83,15 +83,15 @@ class Command(BaseCommand): send_templated_email( template_name="emails/metadata_body.txt", subject_template_name="emails/metadata_subject.txt", - to_address=settings.DEFAULT_FROM_EMAIL, - # to_address="rebecca.hsieh@truss.works ", # TODO: Update to settings.DEFAULT_FROM_EMAIL once tested + # to_address=settings.DEFAULT_FROM_EMAIL, # TODO: Uncomment this when ready to merge + to_address="rebecca.hsieh@truss.works ", context={"current_date_str": current_date_str}, file=encrypted_metadata_in_bytes, ) def _encrypt_metadata(self, input_file, output_file, password): current_date = datetime.now().strftime("%m%d%Y") - current_filename = f"domain-metadata-{current_date}.txt" + current_filename = f"domain-metadata-{current_date}.csv" # Using ZIP_DEFLATED bc it's a more common compression method supported by most zip utilities and faster # We could also use compression=pyzipper.ZIP_LZMA if we are looking for smaller file size with pyzipper.AESZipFile( diff --git a/src/registrar/tests/test_emails.py b/src/registrar/tests/test_emails.py index f2a94a186..292fe5b1c 100644 --- a/src/registrar/tests/test_emails.py +++ b/src/registrar/tests/test_emails.py @@ -5,7 +5,8 @@ from unittest.mock import MagicMock from django.test import TestCase from .common import completed_application, less_console_noise - +from datetime import datetime +from registrar.utility import email import boto3_mocking # type: ignore @@ -182,3 +183,33 @@ class TestEmails(TestCase): self.assertNotIn("Anything else", body) # spacing should be right between adjacent elements self.assertRegex(body, r"5557\n\n----") + + @boto3_mocking.patching + def test_send_email_with_attachment(self): + with boto3_mocking.clients.handler_for("ses", self.mock_client_class): + sender_email = "sender@example.com" + recipient_email = "recipient@example.com" + subject = "Test Subject" + body = "Test Body" + attachment_file = b"Attachment file content" + current_date = datetime.now().strftime("%m%d%Y") + current_filename = f"domain-metadata-{current_date}.zip" + + response = email.send_email_with_attachment( + sender_email, recipient_email, subject, body, attachment_file, self.mock_client + ) + print("response is", response) + # Assert that the `send_raw_email` method of the mocked SES client was called with the expected params + self.mock_client.send_raw_email.assert_called_once() + + # Get the args passed to the `send_raw_email` method + call_args = self.mock_client.send_raw_email.call_args[1] + print("call_args is", call_args) + + # Assert that the attachment filename is correct + self.assertEqual(call_args["RawMessage"]["Data"].count(f'filename="{current_filename}"'), 1) + + # Assert that the attachment content is encrypted + self.assertIn("Content-Type: application/octet-stream", call_args["RawMessage"]["Data"]) + self.assertIn("Content-Transfer-Encoding: base64", call_args["RawMessage"]["Data"]) + self.assertIn("Content-Disposition: attachment;", call_args["RawMessage"]["Data"]) diff --git a/src/registrar/utility/email.py b/src/registrar/utility/email.py index a81a41716..ddd211041 100644 --- a/src/registrar/utility/email.py +++ b/src/registrar/utility/email.py @@ -65,11 +65,11 @@ def send_templated_email(template_name: str, subject_template_name: str, to_addr aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY, config=settings.BOTO_CONFIG, ) - # Define the subject line with the current date response = send_email_with_attachment( settings.DEFAULT_FROM_EMAIL, to_address, subject, email_body, file, ses_client ) - # TODO: Remove this print statement + # TODO: Remove this print statement when ready to merge, + # leaving rn for getting error codes in case print("Response from send_email_with_attachment_is:", response) except Exception as exc: raise EmailSendingError("Could not send SES email.") from exc