Allow no-auth SMTP connection for email notifications (#759)

* DEFAULT_FROM_EMAIL -> EMAIL_FROM

* add EMAIL_FROM live setting

* EMAIL_FROM -> EMAIL_FROM_ADDRESS

* merge dev

* add test for get_from_email

* allow live settings to be null on internal API

* remove redundant tests
This commit is contained in:
Vadim Stepanov 2022-11-03 16:18:37 +00:00 committed by GitHub
parent e9df5ab5c9
commit e2456315af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 41 additions and 66 deletions

View file

@ -195,6 +195,7 @@ Grafana OnCall is capable of sending emails using SMTP as a user notification st
- `EMAIL_HOST_USER` - SMTP server user
- `EMAIL_HOST_PASSWORD` - SMTP server password
- `EMAIL_PORT` (default is `587`) - SMTP server port
- `EMAIL_USE_TLS` (default is `True`) - to enable/disable TLS
- `EMAIL_USE_TLS` (default is `True`) - To enable/disable TLS
- `EMAIL_FROM_ADDRESS` (optional) - Email address used to send emails. If not specified, `EMAIL_HOST_USER` will be used.
After enabling the email integration, it will be possible to use the `Notify by email` notification step in user settings.

View file

@ -5,7 +5,7 @@ from apps.base.models import LiveSetting
class LiveSettingSerializer(serializers.ModelSerializer):
id = serializers.CharField(read_only=True, source="public_primary_key")
value = serializers.JSONField(allow_null=False)
value = serializers.JSONField(allow_null=True)
class Meta:
model = LiveSetting

View file

@ -38,6 +38,7 @@ class LiveSetting(models.Model):
"EMAIL_HOST_USER",
"EMAIL_HOST_PASSWORD",
"EMAIL_USE_TLS",
"EMAIL_FROM_ADDRESS",
"TWILIO_ACCOUNT_SID",
"TWILIO_AUTH_TOKEN",
"TWILIO_NUMBER",
@ -61,6 +62,7 @@ class LiveSetting(models.Model):
"EMAIL_HOST_USER": "SMTP server user",
"EMAIL_HOST_PASSWORD": "SMTP server password",
"EMAIL_USE_TLS": "SMTP enable/disable TLS",
"EMAIL_FROM_ADDRESS": "Email address used to send emails. If not specified, EMAIL_HOST_USER will be used.",
"SLACK_SIGNING_SECRET": (
"Check <a href='"
"https://grafana.com/docs/grafana-cloud/oncall/open-source/#slack-setup"

View file

@ -16,6 +16,16 @@ MAX_RETRIES = 1 if settings.DEBUG else 10
logger = get_task_logger(__name__)
def get_from_email(user):
if live_settings.EMAIL_FROM_ADDRESS:
return live_settings.EMAIL_FROM_ADDRESS
if settings.LICENSE == settings.CLOUD_LICENSE_NAME:
return "oncall@{}.grafana.net".format(user.organization.stack_slug)
return live_settings.EMAIL_HOST_USER
@shared_dedicated_queue_retry_task(autoretry_for=(Exception,), retry_backoff=True, max_retries=MAX_RETRIES)
def notify_user_async(user_pk, alert_group_pk, notification_policy_pk):
# imported here to avoid circular import error
@ -76,13 +86,9 @@ def notify_user_async(user_pk, alert_group_pk, notification_policy_pk):
subject, html_message = build_subject_and_message(alert_group, emails_left)
message = strip_tags(html_message)
from_email = get_from_email(user)
recipient_list = [user.email]
if settings.LICENSE == settings.CLOUD_LICENSE_NAME:
email_from = "oncall@{}.grafana.net".format(user.organization.stack_slug)
else:
email_from = live_settings.EMAIL_HOST_USER
connection = get_connection(
host=live_settings.EMAIL_HOST,
port=live_settings.EMAIL_PORT,
@ -94,7 +100,7 @@ def notify_user_async(user_pk, alert_group_pk, notification_policy_pk):
)
try:
send_mail(subject, message, email_from, recipient_list, html_message=html_message, connection=connection)
send_mail(subject, message, from_email, recipient_list, html_message=html_message, connection=connection)
EmailMessage.objects.create(
represents_alert_group=alert_group,
notification_policy=notification_policy,

View file

@ -6,7 +6,7 @@ from django.core import mail
from django.core.mail.backends.locmem import EmailBackend
from apps.base.models import UserNotificationPolicy, UserNotificationPolicyLogRecord
from apps.email.tasks import notify_user_async
from apps.email.tasks import get_from_email, notify_user_async
from apps.user_management.subscription_strategy.free_public_beta_subscription_strategy import (
FreePublicBetaSubscriptionStrategy,
)
@ -158,69 +158,35 @@ def test_notify_user_no_emails_left(
@pytest.mark.django_db
def test_notify_user_from_email_oss(
@pytest.mark.parametrize(
"license_name,email_host_user,email_from_address,expected",
[
("Cloud", "user", "from_address", "from_address"),
("OpenSource", "user", "from_address", "from_address"),
("Cloud", "user", None, "oncall@slug.grafana.net"),
("Cloud", None, None, "oncall@slug.grafana.net"),
("OpenSource", "user", None, "user"),
("OpenSource", None, None, None),
],
)
def test_get_from_email(
settings,
make_organization,
make_user_for_organization,
make_token_for_organization,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_user_notification_policy,
license_name,
email_host_user,
email_from_address,
expected,
):
settings.LICENSE = settings.OPEN_SOURCE_LICENSE_NAME
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
settings.EMAIL_HOST = "test"
settings.EMAIL_HOST_USER = "test@test.com"
settings.EMAIL_HOST_PASSWORD = "password"
organization = make_organization(stack_slug="example")
user = make_user_for_organization(organization)
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
notification_policy = make_user_notification_policy(
user,
UserNotificationPolicy.Step.NOTIFY,
notify_by=8,
important=False,
)
notify_user_async(user.pk, alert_group.pk, notification_policy.pk)
assert mail.outbox[0].from_email == "test@test.com"
@pytest.mark.django_db
def test_notify_user_from_email_cloud(
settings,
make_organization,
make_user_for_organization,
make_token_for_organization,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_user_notification_policy,
):
settings.LICENSE = settings.CLOUD_LICENSE_NAME
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
settings.EMAIL_HOST = "test"
settings.LICENSE = license_name
settings.EMAIL_HOST_USER = email_host_user
settings.EMAIL_FROM_ADDRESS = email_from_address
organization = make_organization(stack_slug="slug")
user = make_user_for_organization(organization)
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
notification_policy = make_user_notification_policy(
user,
UserNotificationPolicy.Step.NOTIFY,
notify_by=8,
important=False,
)
notify_user_async(user.pk, alert_group.pk, notification_policy.pk)
assert mail.outbox[0].from_email == "oncall@slug.grafana.net"
assert get_from_email(user) == expected

View file

@ -568,7 +568,7 @@ EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD")
EMAIL_PORT = getenv_integer("EMAIL_PORT", 587)
EMAIL_USE_TLS = getenv_boolean("EMAIL_USE_TLS", True)
DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL")
EMAIL_FROM_ADDRESS = os.getenv("EMAIL_FROM_ADDRESS")
if FEATURE_EMAIL_INTEGRATION_ENABLED:
EXTRA_MESSAGING_BACKENDS = [("apps.email.backend.EmailBackend", 8)]

View file

@ -366,7 +366,7 @@ rabbitmq-password
key: smtp-password
- name: EMAIL_USE_TLS
value: {{ .Values.oncall.smtp.tls | toString | title | quote }}
- name: DEFAULT_FROM_EMAIL
- name: EMAIL_FROM_ADDRESS
value: {{ .Values.oncall.smtp.fromEmail | quote }}
{{- else -}}
- name: FEATURE_EMAIL_INTEGRATION_ENABLED