From f427076598e42c27b9f4bb65fb9b6181dbb88f60 Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Mon, 4 Mar 2024 16:45:05 -0800 Subject: [PATCH] Fix emailing functionality and update subject and body and file names --- .../generate_current_metadata_report.py | 59 +++++++++++-------- .../templates/emails/metadata_body.txt | 1 + .../templates/emails/metadata_subject.txt | 2 + src/registrar/utility/email.py | 37 ++++++------ 4 files changed, 55 insertions(+), 44 deletions(-) create mode 100644 src/registrar/templates/emails/metadata_body.txt create mode 100644 src/registrar/templates/emails/metadata_subject.txt diff --git a/src/registrar/management/commands/generate_current_metadata_report.py b/src/registrar/management/commands/generate_current_metadata_report.py index 1a33c2791..023a19f10 100644 --- a/src/registrar/management/commands/generate_current_metadata_report.py +++ b/src/registrar/management/commands/generate_current_metadata_report.py @@ -4,6 +4,8 @@ import logging import os import pyzipper +from datetime import datetime + from django.core.management import BaseCommand from django.conf import settings from registrar.utility import csv_export @@ -13,9 +15,11 @@ from ...utility.email import send_templated_email, EmailSendingError logger = logging.getLogger(__name__) + class Command(BaseCommand): help = ( - "Generates and uploads a current-metadata.csv file to our S3 bucket " "which is based off of all existing Domains." + "Generates and uploads a current-metadata.csv file to our S3 bucket " + "which is based off of all existing Domains." ) def add_arguments(self, parser): @@ -26,7 +30,7 @@ class Command(BaseCommand): default=True, help="Flag that determines if we do a check for os.path.exists. Used for test cases", ) - + def handle(self, **options): """Grabs the directory then creates current-metadata.csv in that directory""" file_name = "current-metadata.csv" @@ -58,38 +62,43 @@ class Command(BaseCommand): # Upload this generated file for our S3 instance s3_client.upload_file(file_path, file_name) - """ - We want to make sure to upload to s3 for back up - And now we also want to get the file and encrypt it so we can send it in an email - """ - # Encrypt metadata into a zip file + # Set zip file name + current_date = datetime.now().strftime("%m%d%Y") + current_filename = f"domain-metadata-{current_date}.zip" + # Pre-set zip file name + encrypted_metadata_output = current_filename - # pre-setting zip file name - encrypted_metadata_output = 'encrypted_metadata.zip' + # Set context for the subject + current_date_str = datetime.now().strftime("%Y-%m-%d") - # Secret is encrypted into getgov-credentials # TODO: Update secret in getgov-credentials via cloud.gov and my own .env when ready - - # Encrypt the metadata - # TODO: UPDATE SECRET_ENCRYPT_METADATA pw getgov-credentials on stable - encrypted_metadata = self._encrypt_metadata(s3_client.get_file(file_name), encrypted_metadata_output, str.encode(settings.SECRET_ENCRYPT_METADATA)) - print("encrypted_metadata is:", encrypted_metadata) - print("the type is: ", type(encrypted_metadata)) + + # Encrypt the metadata + encrypted_metadata_in_bytes = self._encrypt_metadata( + s3_client.get_file(file_name), encrypted_metadata_output, str.encode(settings.SECRET_ENCRYPT_METADATA) + ) + # Send the metadata file that is zipped - # TODO: Make new .txt files send_templated_email( - "emails/metadata_body.txt", - "emails/metadata_subject.txt", - to_address="rebecca.hsieh@truss.works", # TODO: Update to settings.DEFAULT_FROM_EMAIL once tested - file=encrypted_metadata, + 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 + 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" # 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(output_file, 'w', compression=pyzipper.ZIP_DEFLATED, encryption=pyzipper.WZ_AES) as f_out: + with pyzipper.AESZipFile( + output_file, "w", compression=pyzipper.ZIP_DEFLATED, encryption=pyzipper.WZ_AES + ) as f_out: f_out.setpassword(password) - f_out.writestr('encrypted_metadata.txt', input_file) - return output_file - + f_out.writestr(current_filename, input_file) + with open(output_file, "rb") as file_data: + attachment_in_bytes = file_data.read() + return attachment_in_bytes diff --git a/src/registrar/templates/emails/metadata_body.txt b/src/registrar/templates/emails/metadata_body.txt new file mode 100644 index 000000000..adf0a186c --- /dev/null +++ b/src/registrar/templates/emails/metadata_body.txt @@ -0,0 +1 @@ +An export of all .gov metadata. diff --git a/src/registrar/templates/emails/metadata_subject.txt b/src/registrar/templates/emails/metadata_subject.txt new file mode 100644 index 000000000..5fdece7ef --- /dev/null +++ b/src/registrar/templates/emails/metadata_subject.txt @@ -0,0 +1,2 @@ +Domain metadata - {{current_date_str}} + diff --git a/src/registrar/utility/email.py b/src/registrar/utility/email.py index 5f3e42eb5..a81a41716 100644 --- a/src/registrar/utility/email.py +++ b/src/registrar/utility/email.py @@ -2,6 +2,7 @@ import boto3 import logging +from datetime import datetime from django.conf import settings from django.template.loader import get_template from email.mime.base import MIMEBase @@ -19,7 +20,7 @@ class EmailSendingError(RuntimeError): pass -def send_templated_email(template_name: str, subject_template_name: str, to_address: str, context={}, file: str=None): +def send_templated_email(template_name: str, subject_template_name: str, to_address: str, context={}, file: str = None): """Send an email built from a template to one email address. template_name and subject_template_name are relative to the same template @@ -56,8 +57,7 @@ def send_templated_email(template_name: str, subject_template_name: str, to_addr }, }, ) - if file is not None: - # TODO: Update sender email when we figure out + else: ses_client = boto3.client( "ses", region_name=settings.AWS_REGION, @@ -65,35 +65,34 @@ 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, ) - - #TODO: Update sender to settings.DEFAULT_FROM_EMAIL - response = send_email_with_attachment(settings.DEFAULT_FROM_EMAIL, to_address, subject, email_body, file, ses_client) + # 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 print("Response from send_email_with_attachment_is:", response) except Exception as exc: raise EmailSendingError("Could not send SES email.") from exc + def send_email_with_attachment(sender, recipient, subject, body, attachment_file, ses_client): # Create a multipart/mixed parent container - msg = MIMEMultipart('mixed') - msg['Subject'] = subject - msg['From'] = sender - msg['To'] = recipient + msg = MIMEMultipart("mixed") + msg["Subject"] = subject + msg["From"] = sender + msg["To"] = recipient # Add the text part - text_part = MIMEText(body, 'plain') + text_part = MIMEText(body, "plain") msg.attach(text_part) # Add the attachment part - - # set it into this "type" attachment_part = MIMEApplication(attachment_file) # Adding attachment header + filename that the attachment will be called - attachment_part.add_header('Content-Disposition', f'attachment; filename="encrypted_metadata.zip"') + current_date = datetime.now().strftime("%m%d%Y") + current_filename = f"domain-metadata-{current_date}.zip" + attachment_part.add_header("Content-Disposition", f'attachment; filename="{current_filename}"') msg.attach(attachment_part) - response = ses_client.send_raw_email( - Source=sender, - Destinations=[recipient], - RawMessage={"Data": msg.as_string()} - ) + response = ses_client.send_raw_email(Source=sender, Destinations=[recipient], RawMessage={"Data": msg.as_string()}) return response