Use django constance to add EMAIL_RECIPIENT_WHITELIST

This commit is contained in:
Daniel Egger 2023-08-25 16:39:14 +02:00
parent d83f660918
commit 6badbc480c
7 changed files with 51 additions and 19 deletions

View File

@ -31,7 +31,7 @@ if __name__ == "__main__":
print(csac.due_date) print(csac.due_date)
result = send_email( result = send_email(
to_emails="daniel.egger+sendgrid@gmail.com", recipient_email="daniel.egger+sendgrid@gmail.com",
template=EmailTemplate.ATTENDANCE_COURSE_REMINDER, template=EmailTemplate.ATTENDANCE_COURSE_REMINDER,
template_data=create_template_data_from_course_session_attendance_course(csac), template_data=create_template_data_from_course_session_attendance_course(csac),
template_language="de", template_language="de",

View File

@ -111,6 +111,7 @@ THIRD_PARTY_APPS = [
"graphene_django", "graphene_django",
"notifications", "notifications",
"django_jsonform", "django_jsonform",
"constance",
] ]
LOCAL_APPS = [ LOCAL_APPS = [
@ -687,6 +688,15 @@ WHITENOISE_SKIP_COMPRESS_EXTENSIONS = (
) )
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend"
CONSTANCE_CONFIG = {
"EMAIL_RECIPIENT_WHITELIST": (
"daniel.egger+sendgrid@gmail.com, elia.bieri@iterativ.ch",
"Which emails will recieve emails. Use single `*` for all OR comma separated list of emails. "
"Default value is empty and will not send any emails. (No regex support!)",
),
}
if APP_ENVIRONMENT == "local": if APP_ENVIRONMENT == "local":
# http://whitenoise.evans.io/en/latest/django.html#using-whitenoise-in-development # http://whitenoise.evans.io/en/latest/django.html#using-whitenoise-in-development
INSTALLED_APPS = ["whitenoise.runserver_nostatic"] + INSTALLED_APPS # noqa F405 INSTALLED_APPS = ["whitenoise.runserver_nostatic"] + INSTALLED_APPS # noqa F405

View File

@ -122,6 +122,7 @@ django==3.2.20
# django-modelcluster # django-modelcluster
# django-notifications-hq # django-notifications-hq
# django-permissionedforms # django-permissionedforms
# django-picklefield
# django-redis # django-redis
# django-storages # django-storages
# django-stubs # django-stubs
@ -137,6 +138,8 @@ django==3.2.20
# wagtail-localize # wagtail-localize
django-click==2.3.0 django-click==2.3.0
# via -r requirements.in # via -r requirements.in
django-constance==3.1.0
# via -r requirements.in
django-cors-headers==4.2.0 django-cors-headers==4.2.0
# via -r requirements.in # via -r requirements.in
django-coverage-plugin==3.1.0 django-coverage-plugin==3.1.0
@ -163,6 +166,8 @@ django-notifications-hq==1.8.2
# via -r requirements.in # via -r requirements.in
django-permissionedforms==0.1 django-permissionedforms==0.1
# via wagtail # via wagtail
django-picklefield==3.1
# via django-constance
django-ratelimit==4.1.0 django-ratelimit==4.1.0
# via -r requirements.in # via -r requirements.in
django-redis==5.3.0 django-redis==5.3.0

View File

@ -28,6 +28,7 @@ django-storages
django-storages[azure] django-storages[azure]
django-notifications-hq django-notifications-hq
django-jsonform django-jsonform
django-constance
psycopg2-binary psycopg2-binary
gunicorn gunicorn

View File

@ -84,6 +84,7 @@ django==3.2.20
# django-modelcluster # django-modelcluster
# django-notifications-hq # django-notifications-hq
# django-permissionedforms # django-permissionedforms
# django-picklefield
# django-redis # django-redis
# django-storages # django-storages
# django-taggit # django-taggit
@ -96,6 +97,8 @@ django==3.2.20
# wagtail-localize # wagtail-localize
django-click==2.3.0 django-click==2.3.0
# via -r requirements.in # via -r requirements.in
django-constance==3.1.0
# via -r requirements.in
django-cors-headers==4.2.0 django-cors-headers==4.2.0
# via -r requirements.in # via -r requirements.in
django-csp==3.7 django-csp==3.7
@ -116,6 +119,8 @@ django-notifications-hq==1.8.2
# via -r requirements.in # via -r requirements.in
django-permissionedforms==0.1 django-permissionedforms==0.1
# via wagtail # via wagtail
django-picklefield==3.1
# via django-constance
django-ratelimit==4.1.0 django-ratelimit==4.1.0
# via -r requirements.in # via -r requirements.in
django-redis==5.3.0 django-redis==5.3.0

View File

@ -1,9 +1,9 @@
from enum import Enum from enum import Enum
import structlog import structlog
from constance import config
from django.conf import settings from django.conf import settings
from django.utils import timezone from django.utils import timezone
from sendgrid import Mail, SendGridAPIClient from sendgrid import Mail, SendGridAPIClient
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse
@ -40,27 +40,35 @@ class EmailTemplate(Enum):
def send_email( def send_email(
to_emails: str | list[str], recipient_email: str,
template: EmailTemplate, template: EmailTemplate,
template_data: dict, template_data: dict,
template_language: str = "de", template_language: str = "de",
fail_silently: bool = True, fail_silently: bool = True,
) -> bool: ) -> bool:
log = logger.bind( log = logger.bind(
recipient_emails=to_emails, recipient_email=recipient_email,
template=template.name, template=template.name,
template_data=template_data, template_data=template_data,
template_language=template_language, template_language=template_language,
) )
try: try:
send_sendgrid_email( whitelist_emails = [
to_emails=to_emails, email.strip()
template=template, for email in config.EMAIL_RECIPIENT_WHITELIST.strip().split(",")
template_data=template_data, ]
template_language=template_language, if "*" in whitelist_emails or recipient_email in whitelist_emails:
) _send_sendgrid_email(
log.info("Email sent successfully") recipient_email=recipient_email,
return True template=template,
template_data=template_data,
template_language=template_language,
)
log.info("Email sent successfully")
return True
else:
log.info("Email not sent because recipient is not whitelisted")
return False
except Exception as e: except Exception as e:
log.error( log.error(
"Failed to send Email", exception=str(e), exc_info=True, stack_info=True "Failed to send Email", exception=str(e), exc_info=True, stack_info=True
@ -70,15 +78,15 @@ def send_email(
return False return False
def send_sendgrid_email( def _send_sendgrid_email(
to_emails: str | list[str], recipient_email: str,
template: EmailTemplate, template: EmailTemplate,
template_data: dict, template_data: dict,
template_language: str = "de", template_language: str = "de",
) -> None: ) -> None:
message = Mail( message = Mail(
from_email="noreply@my.vbv-afa.ch", from_email="noreply@my.vbv-afa.ch",
to_emails=to_emails, to_emails=recipient_email,
) )
message.template_id = template.value.get(template_language, template.value["de"]) message.template_id = template.value.get(template_language, template.value["de"])
message.dynamic_template_data = template_data message.dynamic_template_data = template_data

View File

@ -92,8 +92,8 @@ class NotificationService:
else: else:
actor_avatar_url = sender.avatar_url actor_avatar_url = sender.avatar_url
log = logger.bind( log = logger.bind(
recipient=recipient.get_full_name(), recipient=recipient.email,
sender=sender.get_full_name(), sender=sender.email,
verb=verb, verb=verb,
notification_type=notification_type, notification_type=notification_type,
course=course, course=course,
@ -113,6 +113,7 @@ class NotificationService:
emailed = False emailed = False
if cls._should_send_email(notification_type, recipient): if cls._should_send_email(notification_type, recipient):
log.debug("Try to send email")
emailed = cls._send_email( emailed = cls._send_email(
recipient=recipient, recipient=recipient,
template=email_template, template=email_template,
@ -121,6 +122,8 @@ class NotificationService:
**template_data, **template_data,
}, },
) )
else:
log.debug("Should not send email")
try: try:
response = notify.send( response = notify.send(
@ -141,7 +144,7 @@ class NotificationService:
except Exception as e: except Exception as e:
log.error("Failed to send notification", exception=str(e)) log.error("Failed to send notification", exception=str(e))
else: else:
log.info("Notification sent successfully") log.info("Notification sent successfully", emailed=emailed)
@staticmethod @staticmethod
def _should_send_email( def _should_send_email(
@ -158,7 +161,7 @@ class NotificationService:
template_data: dict, template_data: dict,
) -> bool: ) -> bool:
return send_email( return send_email(
to_emails=recipient.email, recipient_email=recipient.email,
template=template, template=template,
template_data=template_data, template_data=template_data,
template_language=recipient.language, template_language=recipient.language,