mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-15 00:57:02 +02:00
merge main again
This commit is contained in:
commit
caa1a9ef84
7 changed files with 128 additions and 40 deletions
|
@ -286,6 +286,7 @@ AWS_MAX_ATTEMPTS = 3
|
||||||
BOTO_CONFIG = Config(retries={"mode": AWS_RETRY_MODE, "max_attempts": AWS_MAX_ATTEMPTS})
|
BOTO_CONFIG = Config(retries={"mode": AWS_RETRY_MODE, "max_attempts": AWS_MAX_ATTEMPTS})
|
||||||
|
|
||||||
# email address to use for various automated correspondence
|
# email address to use for various automated correspondence
|
||||||
|
# also used as a default to and bcc email
|
||||||
DEFAULT_FROM_EMAIL = "help@get.gov <help@get.gov>"
|
DEFAULT_FROM_EMAIL = "help@get.gov <help@get.gov>"
|
||||||
|
|
||||||
# connect to an (external) SMTP server for sending email
|
# connect to an (external) SMTP server for sending email
|
||||||
|
|
|
@ -4,6 +4,7 @@ from typing import Union
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django_fsm import FSMField, transition # type: ignore
|
from django_fsm import FSMField, transition # type: ignore
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -588,7 +589,9 @@ class DomainApplication(TimeStampedModel):
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
logger.error(f"Can't query an approved domain while attempting {called_from}")
|
logger.error(f"Can't query an approved domain while attempting {called_from}")
|
||||||
|
|
||||||
def _send_status_update_email(self, new_status, email_template, email_template_subject, send_email=True):
|
def _send_status_update_email(
|
||||||
|
self, new_status, email_template, email_template_subject, send_email=True, bcc_address=""
|
||||||
|
):
|
||||||
"""Send a status update email to the submitter.
|
"""Send a status update email to the submitter.
|
||||||
|
|
||||||
The email goes to the email address that the submitter gave as their
|
The email goes to the email address that the submitter gave as their
|
||||||
|
@ -613,6 +616,7 @@ class DomainApplication(TimeStampedModel):
|
||||||
email_template_subject,
|
email_template_subject,
|
||||||
self.submitter.email,
|
self.submitter.email,
|
||||||
context={"application": self},
|
context={"application": self},
|
||||||
|
bcc_address=bcc_address,
|
||||||
)
|
)
|
||||||
logger.info(f"The {new_status} email sent to: {self.submitter.email}")
|
logger.info(f"The {new_status} email sent to: {self.submitter.email}")
|
||||||
except EmailSendingError:
|
except EmailSendingError:
|
||||||
|
@ -654,11 +658,17 @@ class DomainApplication(TimeStampedModel):
|
||||||
# Limit email notifications to transitions from Started and Withdrawn
|
# Limit email notifications to transitions from Started and Withdrawn
|
||||||
limited_statuses = [self.ApplicationStatus.STARTED, self.ApplicationStatus.WITHDRAWN]
|
limited_statuses = [self.ApplicationStatus.STARTED, self.ApplicationStatus.WITHDRAWN]
|
||||||
|
|
||||||
|
bcc_address = ""
|
||||||
|
if settings.IS_PRODUCTION:
|
||||||
|
bcc_address = settings.DEFAULT_FROM_EMAIL
|
||||||
|
|
||||||
if self.status in limited_statuses:
|
if self.status in limited_statuses:
|
||||||
self._send_status_update_email(
|
self._send_status_update_email(
|
||||||
"submission confirmation",
|
"submission confirmation",
|
||||||
"emails/submission_confirmation.txt",
|
"emails/submission_confirmation.txt",
|
||||||
"emails/submission_confirmation_subject.txt",
|
"emails/submission_confirmation_subject.txt",
|
||||||
|
True,
|
||||||
|
bcc_address,
|
||||||
)
|
)
|
||||||
|
|
||||||
@transition(
|
@transition(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from django.test import TestCase, RequestFactory, Client
|
from django.test import TestCase, RequestFactory, Client, override_settings
|
||||||
from django.contrib.admin.sites import AdminSite
|
from django.contrib.admin.sites import AdminSite
|
||||||
from contextlib import ExitStack
|
from contextlib import ExitStack
|
||||||
from django_webtest import WebTest # type: ignore
|
from django_webtest import WebTest # type: ignore
|
||||||
|
@ -608,7 +608,9 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
# Use the model admin's save_model method
|
# Use the model admin's save_model method
|
||||||
self.admin.save_model(request, application, form=None, change=True)
|
self.admin.save_model(request, application, form=None, change=True)
|
||||||
|
|
||||||
def assert_email_is_accurate(self, expected_string, email_index, email_address):
|
def assert_email_is_accurate(
|
||||||
|
self, expected_string, email_index, email_address, test_that_no_bcc=False, bcc_email_address=""
|
||||||
|
):
|
||||||
"""Helper method for the email test cases.
|
"""Helper method for the email test cases.
|
||||||
email_index is the index of the email in mock_client."""
|
email_index is the index of the email in mock_client."""
|
||||||
|
|
||||||
|
@ -628,12 +630,26 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
self.assertEqual(to_email, email_address)
|
self.assertEqual(to_email, email_address)
|
||||||
self.assertIn(expected_string, email_body)
|
self.assertIn(expected_string, email_body)
|
||||||
|
|
||||||
|
if test_that_no_bcc:
|
||||||
|
_ = ""
|
||||||
|
with self.assertRaises(KeyError):
|
||||||
|
with less_console_noise():
|
||||||
|
_ = kwargs["Destination"]["BccAddresses"][0]
|
||||||
|
self.assertEqual(_, "")
|
||||||
|
|
||||||
|
if bcc_email_address:
|
||||||
|
bcc_email = kwargs["Destination"]["BccAddresses"][0]
|
||||||
|
self.assertEqual(bcc_email, bcc_email_address)
|
||||||
|
|
||||||
def test_save_model_sends_submitted_email(self):
|
def test_save_model_sends_submitted_email(self):
|
||||||
"""When transitioning to submitted from started or withdrawn on a domain request,
|
"""When transitioning to submitted from started or withdrawn on a domain request,
|
||||||
an email is sent out.
|
an email is sent out.
|
||||||
|
|
||||||
When transitioning to submitted from dns needed or in review on a domain request,
|
When transitioning to submitted from dns needed or in review on a domain request,
|
||||||
no email is sent out."""
|
no email is sent out.
|
||||||
|
|
||||||
|
Also test that the default email set in settings is NOT BCCd on non-prod whenever
|
||||||
|
an email does go out."""
|
||||||
|
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
# Ensure there is no user with this email
|
# Ensure there is no user with this email
|
||||||
|
@ -645,7 +661,64 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
# Test Submitted Status from started
|
# Test Submitted Status from started
|
||||||
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
|
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
|
||||||
self.assert_email_is_accurate("We received your .gov domain request.", 0, EMAIL)
|
self.assert_email_is_accurate("We received your .gov domain request.", 0, EMAIL, True)
|
||||||
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
||||||
|
|
||||||
|
# Test Withdrawn Status
|
||||||
|
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||||
|
self.assert_email_is_accurate(
|
||||||
|
"Your .gov domain request has been withdrawn and will not be reviewed by our team.", 1, EMAIL, True
|
||||||
|
)
|
||||||
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
|
||||||
|
|
||||||
|
# Test Submitted Status Again (from withdrawn)
|
||||||
|
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
|
||||||
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
|
||||||
|
|
||||||
|
# Move it to IN_REVIEW
|
||||||
|
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
|
||||||
|
|
||||||
|
# Test Submitted Status Again from in IN_REVIEW, no new email should be sent
|
||||||
|
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
|
||||||
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
|
||||||
|
|
||||||
|
# Move it to IN_REVIEW
|
||||||
|
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
|
||||||
|
|
||||||
|
# Move it to ACTION_NEEDED
|
||||||
|
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||||
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
|
||||||
|
|
||||||
|
# Test Submitted Status Again from in ACTION_NEEDED, no new email should be sent
|
||||||
|
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
|
||||||
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
|
||||||
|
|
||||||
|
@override_settings(IS_PRODUCTION=True)
|
||||||
|
def test_save_model_sends_submitted_email_with_bcc_on_prod(self):
|
||||||
|
"""When transitioning to submitted from started or withdrawn on a domain request,
|
||||||
|
an email is sent out.
|
||||||
|
|
||||||
|
When transitioning to submitted from dns needed or in review on a domain request,
|
||||||
|
no email is sent out.
|
||||||
|
|
||||||
|
Also test that the default email set in settings IS BCCd on prod whenever
|
||||||
|
an email does go out."""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
|
# Ensure there is no user with this email
|
||||||
|
EMAIL = "mayor@igorville.gov"
|
||||||
|
User.objects.filter(email=EMAIL).delete()
|
||||||
|
|
||||||
|
BCC_EMAIL = settings.DEFAULT_FROM_EMAIL
|
||||||
|
|
||||||
|
# Create a sample application
|
||||||
|
application = completed_application()
|
||||||
|
|
||||||
|
# Test Submitted Status from started
|
||||||
|
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
|
||||||
|
self.assert_email_is_accurate("We received your .gov domain request.", 0, EMAIL, False, BCC_EMAIL)
|
||||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
||||||
|
|
||||||
# Test Withdrawn Status
|
# Test Withdrawn Status
|
||||||
|
@ -657,6 +730,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
# Test Submitted Status Again (from withdrawn)
|
# Test Submitted Status Again (from withdrawn)
|
||||||
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
|
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
|
||||||
|
self.assert_email_is_accurate("We received your .gov domain request.", 0, EMAIL, False, BCC_EMAIL)
|
||||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
|
||||||
|
|
||||||
# Move it to IN_REVIEW
|
# Move it to IN_REVIEW
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
from django.test import Client, TestCase, override_settings
|
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
|
|
||||||
|
|
||||||
class MyTestCase(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.client = Client()
|
|
||||||
username = "test_user"
|
|
||||||
first_name = "First"
|
|
||||||
last_name = "Last"
|
|
||||||
email = "info@example.com"
|
|
||||||
self.user = get_user_model().objects.create(
|
|
||||||
username=username, first_name=first_name, last_name=last_name, email=email
|
|
||||||
)
|
|
||||||
self.client.force_login(self.user)
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
super().tearDown()
|
|
||||||
self.user.delete()
|
|
||||||
|
|
||||||
@override_settings(IS_PRODUCTION=True)
|
|
||||||
def test_production_environment(self):
|
|
||||||
"""No banner on prod."""
|
|
||||||
home_page = self.client.get("/")
|
|
||||||
self.assertNotContains(home_page, "You are on a test site.")
|
|
||||||
|
|
||||||
@override_settings(IS_PRODUCTION=False)
|
|
||||||
def test_non_production_environment(self):
|
|
||||||
"""Banner on non-prod."""
|
|
||||||
home_page = self.client.get("/")
|
|
||||||
self.assertContains(home_page, "You are on a test site.")
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.test import Client, TestCase
|
from django.test import Client, TestCase, override_settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
from .common import MockEppLib # type: ignore
|
from .common import MockEppLib # type: ignore
|
||||||
|
@ -50,3 +50,32 @@ class TestWithUser(MockEppLib):
|
||||||
DomainApplication.objects.all().delete()
|
DomainApplication.objects.all().delete()
|
||||||
DomainInformation.objects.all().delete()
|
DomainInformation.objects.all().delete()
|
||||||
self.user.delete()
|
self.user.delete()
|
||||||
|
|
||||||
|
|
||||||
|
class TestEnvironmentVariablesEffects(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.client = Client()
|
||||||
|
username = "test_user"
|
||||||
|
first_name = "First"
|
||||||
|
last_name = "Last"
|
||||||
|
email = "info@example.com"
|
||||||
|
self.user = get_user_model().objects.create(
|
||||||
|
username=username, first_name=first_name, last_name=last_name, email=email
|
||||||
|
)
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super().tearDown()
|
||||||
|
self.user.delete()
|
||||||
|
|
||||||
|
@override_settings(IS_PRODUCTION=True)
|
||||||
|
def test_production_environment(self):
|
||||||
|
"""No banner on prod."""
|
||||||
|
home_page = self.client.get("/")
|
||||||
|
self.assertNotContains(home_page, "You are on a test site.")
|
||||||
|
|
||||||
|
@override_settings(IS_PRODUCTION=False)
|
||||||
|
def test_non_production_environment(self):
|
||||||
|
"""Banner on non-prod."""
|
||||||
|
home_page = self.client.get("/")
|
||||||
|
self.assertContains(home_page, "You are on a test site.")
|
||||||
|
|
|
@ -15,7 +15,7 @@ class EmailSendingError(RuntimeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def send_templated_email(template_name: str, subject_template_name: str, to_address: str, context={}):
|
def send_templated_email(template_name: str, subject_template_name: str, to_address: str, bcc_address="", context={}):
|
||||||
"""Send an email built from a template to one email address.
|
"""Send an email built from a template to one email address.
|
||||||
|
|
||||||
template_name and subject_template_name are relative to the same template
|
template_name and subject_template_name are relative to the same template
|
||||||
|
@ -40,10 +40,14 @@ def send_templated_email(template_name: str, subject_template_name: str, to_addr
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise EmailSendingError("Could not access the SES client.") from exc
|
raise EmailSendingError("Could not access the SES client.") from exc
|
||||||
|
|
||||||
|
destination = {"ToAddresses": [to_address]}
|
||||||
|
if bcc_address:
|
||||||
|
destination["BccAddresses"] = [bcc_address]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ses_client.send_email(
|
ses_client.send_email(
|
||||||
FromEmailAddress=settings.DEFAULT_FROM_EMAIL,
|
FromEmailAddress=settings.DEFAULT_FROM_EMAIL,
|
||||||
Destination={"ToAddresses": [to_address]},
|
Destination=destination,
|
||||||
Content={
|
Content={
|
||||||
"Simple": {
|
"Simple": {
|
||||||
"Subject": {"Data": subject},
|
"Subject": {"Data": subject},
|
||||||
|
|
|
@ -14,6 +14,7 @@ from django.http import HttpResponseRedirect
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.views.generic.edit import FormMixin
|
from django.views.generic.edit import FormMixin
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from registrar.models import (
|
from registrar.models import (
|
||||||
Domain,
|
Domain,
|
||||||
|
@ -707,7 +708,7 @@ class DomainAddUserView(DomainFormBaseView):
|
||||||
adding a success message to the view if the email sending succeeds"""
|
adding a success message to the view if the email sending succeeds"""
|
||||||
|
|
||||||
# Set a default email address to send to for staff
|
# Set a default email address to send to for staff
|
||||||
requestor_email = "help@get.gov"
|
requestor_email = settings.DEFAULT_FROM_EMAIL
|
||||||
|
|
||||||
# Check if the email requestor has a valid email address
|
# Check if the email requestor has a valid email address
|
||||||
if not requestor.is_staff and requestor.email is not None and requestor.email.strip() != "":
|
if not requestor.is_staff and requestor.email is not None and requestor.email.strip() != "":
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue