1414import ssl
1515import sys
1616import threading
17+ import os
1718
1819from socket import gaierror , timeout
1920from OpenSSL import crypto as SSLCrypto
@@ -38,6 +39,10 @@ class MailDeliveryException(Exception):
3839 """Specific exception subclass for mail delivery errors"""
3940
4041
42+ class MailDeliveryWhitelistException (MailDeliveryException ):
43+ """Specific exception subclass for non whitelisted mail delivery attempts"""
44+
45+
4146def make_wrap_property (name ):
4247 return property (
4348 lambda self : getattr (self .__obj__ , name ),
@@ -406,6 +411,8 @@ def connect(self, host=None, port=None, user=None, password=None, encryption=Non
406411 _ ("Please define at least one SMTP server, "
407412 "or provide the SMTP parameters explicitly." )))
408413
414+ self ._is_allowed_to_send (smtp_server , raise_exception = True )
415+
409416 if smtp_encryption == 'ssl' :
410417 if 'SMTP_SSL' not in smtplib .__all__ :
411418 raise UserError (
@@ -710,14 +717,6 @@ def send_email(self, message, mail_server_id=None, smtp_server=None, smtp_port=N
710717 _test_logger .info ("skip sending email in test mode" )
711718 return message ['Message-Id' ]
712719
713- # Some odoo tests in base explicitly patch ir.mail_server to return False from _is_test_mode()
714- if (
715- not db_whitelisted (self .env .cr .dbname )
716- and not isinstance (self .connect , MagicMock )
717- and smtp .__class__ .__name__ != "FakeSMTP"
718- ):
719- raise UserError (_ ("Whitelist Error" ) + "\n " + _ ("Database cannot send emails as it is not on the whitelist." ))
720-
721720 try :
722721 message_id = message ['Message-Id' ]
723722
@@ -741,6 +740,8 @@ def send_email(self, message, mail_server_id=None, smtp_server=None, smtp_port=N
741740 smtp .quit ()
742741 except smtplib .SMTPServerDisconnected :
743742 raise
743+ except MailDeliveryWhitelistException :
744+ raise
744745 except Exception as e :
745746 params = (ustr (smtp_server ), e .__class__ .__name__ , e )
746747 msg = _ ("Mail delivery failed via SMTP server '%s'.\n %s: %s" , * params )
@@ -855,3 +856,45 @@ def _is_test_mode(self):
855856 outgoing mail server.
856857 """
857858 return getattr (threading .current_thread (), 'testing' , False ) or self .env .registry .in_test_mode ()
859+
860+ def _is_allowed_to_send (self , smtp_server : str = None , raise_exception : bool = False ) -> bool :
861+ """
862+ Return True if the database is allowed to send email.
863+
864+ Emails are not allowed unless the database is whitelisted by using the
865+ `db_cron_whitelist` option in the odoo config file.
866+
867+ During development, we don't want to enable this option, and we do not want
868+ to send emails to real users, but we do want to test emails using local
869+ development mail servers such as MailHog.
870+
871+ To avoid accidentally allowing real local email servers such as postfix
872+ to send emails, the mail server must be explicitly configured with the
873+ environment variable `ODOO_DEV_SMTP_SERVER` if the database is not
874+ whitelisted.
875+ """
876+
877+ if db_whitelisted (self .env .cr .dbname ):
878+ return True
879+
880+ # Some odoo tests in base explicitly patch ir.mail_server to return False from
881+ # _is_test_mode() in which case we want to allow sending mails.
882+ if isinstance (self .connect , MagicMock ):
883+ return True
884+
885+ dev_smtp_server = os .environ .get ("ODOO_DEV_SMTP_SERVER" )
886+ if dev_smtp_server in ["localhost" , "127.0.0.1" ] and (
887+ (smtp_server and smtp_server == dev_smtp_server )
888+ or
889+ (len (self ) <= 1 and self .smtp_host == dev_smtp_server )
890+ ):
891+ _logger .info ("Allowing local development SMTP server: %s" , smtp_server )
892+ return True
893+
894+ msg = _ ("Database cannot send emails as it is not on the whitelist." )
895+ _logger .warning (msg )
896+
897+ if raise_exception :
898+ raise MailDeliveryWhitelistException (msg )
899+
900+ return False
0 commit comments