From 6badbc480c30494f219a69c7e904f0d75d856b46 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Fri, 25 Aug 2023 16:39:14 +0200 Subject: [PATCH] Use django constance to add EMAIL_RECIPIENT_WHITELIST --- scripts/send_sendgrid_email.py | 2 +- server/config/settings/base.py | 10 ++++++ server/requirements/requirements-dev.txt | 5 +++ server/requirements/requirements.in | 1 + server/requirements/requirements.txt | 5 +++ .../notify/email/email_services.py | 36 +++++++++++-------- server/vbv_lernwelt/notify/services.py | 11 +++--- 7 files changed, 51 insertions(+), 19 deletions(-) diff --git a/scripts/send_sendgrid_email.py b/scripts/send_sendgrid_email.py index 7a157eab..c3dcb501 100644 --- a/scripts/send_sendgrid_email.py +++ b/scripts/send_sendgrid_email.py @@ -31,7 +31,7 @@ if __name__ == "__main__": print(csac.due_date) result = send_email( - to_emails="daniel.egger+sendgrid@gmail.com", + recipient_email="daniel.egger+sendgrid@gmail.com", template=EmailTemplate.ATTENDANCE_COURSE_REMINDER, template_data=create_template_data_from_course_session_attendance_course(csac), template_language="de", diff --git a/server/config/settings/base.py b/server/config/settings/base.py index 1465ca93..3872dbdc 100644 --- a/server/config/settings/base.py +++ b/server/config/settings/base.py @@ -111,6 +111,7 @@ THIRD_PARTY_APPS = [ "graphene_django", "notifications", "django_jsonform", + "constance", ] LOCAL_APPS = [ @@ -687,6 +688,15 @@ WHITENOISE_SKIP_COMPRESS_EXTENSIONS = ( ) 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": # http://whitenoise.evans.io/en/latest/django.html#using-whitenoise-in-development INSTALLED_APPS = ["whitenoise.runserver_nostatic"] + INSTALLED_APPS # noqa F405 diff --git a/server/requirements/requirements-dev.txt b/server/requirements/requirements-dev.txt index 71898040..a66e01e8 100644 --- a/server/requirements/requirements-dev.txt +++ b/server/requirements/requirements-dev.txt @@ -122,6 +122,7 @@ django==3.2.20 # django-modelcluster # django-notifications-hq # django-permissionedforms + # django-picklefield # django-redis # django-storages # django-stubs @@ -137,6 +138,8 @@ django==3.2.20 # wagtail-localize django-click==2.3.0 # via -r requirements.in +django-constance==3.1.0 + # via -r requirements.in django-cors-headers==4.2.0 # via -r requirements.in django-coverage-plugin==3.1.0 @@ -163,6 +166,8 @@ django-notifications-hq==1.8.2 # via -r requirements.in django-permissionedforms==0.1 # via wagtail +django-picklefield==3.1 + # via django-constance django-ratelimit==4.1.0 # via -r requirements.in django-redis==5.3.0 diff --git a/server/requirements/requirements.in b/server/requirements/requirements.in index a47be82c..ae9264d7 100644 --- a/server/requirements/requirements.in +++ b/server/requirements/requirements.in @@ -28,6 +28,7 @@ django-storages django-storages[azure] django-notifications-hq django-jsonform +django-constance psycopg2-binary gunicorn diff --git a/server/requirements/requirements.txt b/server/requirements/requirements.txt index 30ae967d..1261d7d5 100644 --- a/server/requirements/requirements.txt +++ b/server/requirements/requirements.txt @@ -84,6 +84,7 @@ django==3.2.20 # django-modelcluster # django-notifications-hq # django-permissionedforms + # django-picklefield # django-redis # django-storages # django-taggit @@ -96,6 +97,8 @@ django==3.2.20 # wagtail-localize django-click==2.3.0 # via -r requirements.in +django-constance==3.1.0 + # via -r requirements.in django-cors-headers==4.2.0 # via -r requirements.in django-csp==3.7 @@ -116,6 +119,8 @@ django-notifications-hq==1.8.2 # via -r requirements.in django-permissionedforms==0.1 # via wagtail +django-picklefield==3.1 + # via django-constance django-ratelimit==4.1.0 # via -r requirements.in django-redis==5.3.0 diff --git a/server/vbv_lernwelt/notify/email/email_services.py b/server/vbv_lernwelt/notify/email/email_services.py index 91be1747..d62d3421 100644 --- a/server/vbv_lernwelt/notify/email/email_services.py +++ b/server/vbv_lernwelt/notify/email/email_services.py @@ -1,9 +1,9 @@ from enum import Enum import structlog +from constance import config from django.conf import settings from django.utils import timezone - from sendgrid import Mail, SendGridAPIClient from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse @@ -40,27 +40,35 @@ class EmailTemplate(Enum): def send_email( - to_emails: str | list[str], + recipient_email: str, template: EmailTemplate, template_data: dict, template_language: str = "de", fail_silently: bool = True, ) -> bool: log = logger.bind( - recipient_emails=to_emails, + recipient_email=recipient_email, template=template.name, template_data=template_data, template_language=template_language, ) try: - send_sendgrid_email( - to_emails=to_emails, - template=template, - template_data=template_data, - template_language=template_language, - ) - log.info("Email sent successfully") - return True + whitelist_emails = [ + email.strip() + for email in config.EMAIL_RECIPIENT_WHITELIST.strip().split(",") + ] + if "*" in whitelist_emails or recipient_email in whitelist_emails: + _send_sendgrid_email( + recipient_email=recipient_email, + 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: log.error( "Failed to send Email", exception=str(e), exc_info=True, stack_info=True @@ -70,15 +78,15 @@ def send_email( return False -def send_sendgrid_email( - to_emails: str | list[str], +def _send_sendgrid_email( + recipient_email: str, template: EmailTemplate, template_data: dict, template_language: str = "de", ) -> None: message = Mail( 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.dynamic_template_data = template_data diff --git a/server/vbv_lernwelt/notify/services.py b/server/vbv_lernwelt/notify/services.py index 4a493dff..af329da7 100644 --- a/server/vbv_lernwelt/notify/services.py +++ b/server/vbv_lernwelt/notify/services.py @@ -92,8 +92,8 @@ class NotificationService: else: actor_avatar_url = sender.avatar_url log = logger.bind( - recipient=recipient.get_full_name(), - sender=sender.get_full_name(), + recipient=recipient.email, + sender=sender.email, verb=verb, notification_type=notification_type, course=course, @@ -113,6 +113,7 @@ class NotificationService: emailed = False if cls._should_send_email(notification_type, recipient): + log.debug("Try to send email") emailed = cls._send_email( recipient=recipient, template=email_template, @@ -121,6 +122,8 @@ class NotificationService: **template_data, }, ) + else: + log.debug("Should not send email") try: response = notify.send( @@ -141,7 +144,7 @@ class NotificationService: except Exception as e: log.error("Failed to send notification", exception=str(e)) else: - log.info("Notification sent successfully") + log.info("Notification sent successfully", emailed=emailed) @staticmethod def _should_send_email( @@ -158,7 +161,7 @@ class NotificationService: template_data: dict, ) -> bool: return send_email( - to_emails=recipient.email, + recipient_email=recipient.email, template=template, template_data=template_data, template_language=recipient.language,