mirror of https://github.com/apache/superset.git
feat: adds TLS certificate validation option for SMTP (#21272)
This commit is contained in:
parent
994f327157
commit
9fd752057e
|
@ -126,6 +126,7 @@ SLACK_API_TOKEN = "xoxb-"
|
||||||
# Email configuration
|
# Email configuration
|
||||||
SMTP_HOST = "smtp.sendgrid.net" #change to your host
|
SMTP_HOST = "smtp.sendgrid.net" #change to your host
|
||||||
SMTP_STARTTLS = True
|
SMTP_STARTTLS = True
|
||||||
|
SMTP_SSL_SERVER_AUTH = True # If your using an SMTP server with a valid certificate
|
||||||
SMTP_SSL = False
|
SMTP_SSL = False
|
||||||
SMTP_USER = "your_user"
|
SMTP_USER = "your_user"
|
||||||
SMTP_PORT = 2525 # your port eg. 587
|
SMTP_PORT = 2525 # your port eg. 587
|
||||||
|
|
|
@ -987,7 +987,9 @@ SMTP_USER = "superset"
|
||||||
SMTP_PORT = 25
|
SMTP_PORT = 25
|
||||||
SMTP_PASSWORD = "superset"
|
SMTP_PASSWORD = "superset"
|
||||||
SMTP_MAIL_FROM = "superset@superset.com"
|
SMTP_MAIL_FROM = "superset@superset.com"
|
||||||
|
# If True creates a default SSL context with ssl.Purpose.CLIENT_AUTH using the
|
||||||
|
# default system root CA certificates.
|
||||||
|
SMTP_SSL_SERVER_AUTH = False
|
||||||
ENABLE_CHUNK_ENCODING = False
|
ENABLE_CHUNK_ENCODING = False
|
||||||
|
|
||||||
# Whether to bump the logging level to ERROR on the flask_appbuilder package
|
# Whether to bump the logging level to ERROR on the flask_appbuilder package
|
||||||
|
|
|
@ -27,6 +27,7 @@ import platform
|
||||||
import re
|
import re
|
||||||
import signal
|
import signal
|
||||||
import smtplib
|
import smtplib
|
||||||
|
import ssl
|
||||||
import tempfile
|
import tempfile
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -994,23 +995,28 @@ def send_mime_email(
|
||||||
smtp_password = config["SMTP_PASSWORD"]
|
smtp_password = config["SMTP_PASSWORD"]
|
||||||
smtp_starttls = config["SMTP_STARTTLS"]
|
smtp_starttls = config["SMTP_STARTTLS"]
|
||||||
smtp_ssl = config["SMTP_SSL"]
|
smtp_ssl = config["SMTP_SSL"]
|
||||||
|
smpt_ssl_server_auth = config["SMTP_SSL_SERVER_AUTH"]
|
||||||
|
|
||||||
if not dryrun:
|
if dryrun:
|
||||||
|
logger.info("Dryrun enabled, email notification content is below:")
|
||||||
|
logger.info(mime_msg.as_string())
|
||||||
|
return
|
||||||
|
|
||||||
|
# Default ssl context is SERVER_AUTH using the default system
|
||||||
|
# root CA certificates
|
||||||
|
ssl_context = ssl.create_default_context() if smpt_ssl_server_auth else None
|
||||||
smtp = (
|
smtp = (
|
||||||
smtplib.SMTP_SSL(smtp_host, smtp_port)
|
smtplib.SMTP_SSL(smtp_host, smtp_port, context=ssl_context)
|
||||||
if smtp_ssl
|
if smtp_ssl
|
||||||
else smtplib.SMTP(smtp_host, smtp_port)
|
else smtplib.SMTP(smtp_host, smtp_port)
|
||||||
)
|
)
|
||||||
if smtp_starttls:
|
if smtp_starttls:
|
||||||
smtp.starttls()
|
smtp.starttls(context=ssl_context)
|
||||||
if smtp_user and smtp_password:
|
if smtp_user and smtp_password:
|
||||||
smtp.login(smtp_user, smtp_password)
|
smtp.login(smtp_user, smtp_password)
|
||||||
logger.debug("Sent an email to %s", str(e_to))
|
logger.debug("Sent an email to %s", str(e_to))
|
||||||
smtp.sendmail(e_from, e_to, mime_msg.as_string())
|
smtp.sendmail(e_from, e_to, mime_msg.as_string())
|
||||||
smtp.quit()
|
smtp.quit()
|
||||||
else:
|
|
||||||
logger.info("Dryrun enabled, email notification content is below:")
|
|
||||||
logger.info(mime_msg.as_string())
|
|
||||||
|
|
||||||
|
|
||||||
def get_email_address_list(address_string: str) -> List[str]:
|
def get_email_address_list(address_string: str) -> List[str]:
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
# under the License.
|
# under the License.
|
||||||
"""Unit tests for email service in Superset"""
|
"""Unit tests for email service in Superset"""
|
||||||
import logging
|
import logging
|
||||||
|
import ssl
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
from email.mime.application import MIMEApplication
|
from email.mime.application import MIMEApplication
|
||||||
|
@ -175,9 +176,35 @@ class TestEmailSmtp(SupersetTestCase):
|
||||||
utils.send_mime_email("from", "to", MIMEMultipart(), app.config, dryrun=False)
|
utils.send_mime_email("from", "to", MIMEMultipart(), app.config, dryrun=False)
|
||||||
assert not mock_smtp.called
|
assert not mock_smtp.called
|
||||||
mock_smtp_ssl.assert_called_with(
|
mock_smtp_ssl.assert_called_with(
|
||||||
app.config["SMTP_HOST"], app.config["SMTP_PORT"]
|
app.config["SMTP_HOST"], app.config["SMTP_PORT"], context=None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@mock.patch("smtplib.SMTP_SSL")
|
||||||
|
@mock.patch("smtplib.SMTP")
|
||||||
|
def test_send_mime_ssl_server_auth(self, mock_smtp, mock_smtp_ssl):
|
||||||
|
app.config["SMTP_SSL"] = True
|
||||||
|
app.config["SMTP_SSL_SERVER_AUTH"] = True
|
||||||
|
mock_smtp.return_value = mock.Mock()
|
||||||
|
mock_smtp_ssl.return_value = mock.Mock()
|
||||||
|
utils.send_mime_email("from", "to", MIMEMultipart(), app.config, dryrun=False)
|
||||||
|
assert not mock_smtp.called
|
||||||
|
mock_smtp_ssl.assert_called_with(
|
||||||
|
app.config["SMTP_HOST"], app.config["SMTP_PORT"], context=mock.ANY
|
||||||
|
)
|
||||||
|
called_context = mock_smtp_ssl.call_args.kwargs["context"]
|
||||||
|
self.assertEqual(called_context.verify_mode, ssl.CERT_REQUIRED)
|
||||||
|
|
||||||
|
@mock.patch("smtplib.SMTP")
|
||||||
|
def test_send_mime_tls_server_auth(self, mock_smtp):
|
||||||
|
app.config["SMTP_STARTTLS"] = True
|
||||||
|
app.config["SMTP_SSL_SERVER_AUTH"] = True
|
||||||
|
mock_smtp.return_value = mock.Mock()
|
||||||
|
mock_smtp.return_value.starttls.return_value = mock.Mock()
|
||||||
|
utils.send_mime_email("from", "to", MIMEMultipart(), app.config, dryrun=False)
|
||||||
|
mock_smtp.return_value.starttls.assert_called_with(context=mock.ANY)
|
||||||
|
called_context = mock_smtp.return_value.starttls.call_args.kwargs["context"]
|
||||||
|
self.assertEqual(called_context.verify_mode, ssl.CERT_REQUIRED)
|
||||||
|
|
||||||
@mock.patch("smtplib.SMTP_SSL")
|
@mock.patch("smtplib.SMTP_SSL")
|
||||||
@mock.patch("smtplib.SMTP")
|
@mock.patch("smtplib.SMTP")
|
||||||
def test_send_mime_noauth(self, mock_smtp, mock_smtp_ssl):
|
def test_send_mime_noauth(self, mock_smtp, mock_smtp_ssl):
|
||||||
|
|
Loading…
Reference in New Issue