Repository: CTFd/CTFd

Issue:
Mail Plugin does not work - UnboundLocalError: local variable 'auth' referenced before assignment
**Environment**:

  - CTFd Version/Commit: master
  - Operating System: Debian
  - Web Browser and Version: Chrome and Firefox current

**What happened?**

Setting up an E-Mail server and afterwards registering to the ctfd will result in no Mails beeing sent as well as an error.

**What did you expect to happen?**

Getting E-Mails from ctfd

**How to reproduce your issue**

Pull current master. Start by serve.py. Configure local smtp server (noauth, port 25, no tls). Register as a user.

**Any associated stack traces or error logs**

Dec 04 14:18:41 cat-dmz-1-web bash[21619]: 192.168.2.1 - - [04/Dec/2018 14:18:41] "GET /socket.io/?EIO=3&transport=polling&t=1543929521041-0 HTTP/1.1" 200 -
Dec 04 14:18:49 cat-dmz-1-web bash[21619]: [2018-12-04 14:18:49,443] ERROR in app: Exception on /register [POST]
Dec 04 14:18:49 cat-dmz-1-web bash[21619]: Traceback (most recent call last):
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:   File "/home/ctfd/.local/lib/python2.7/site-packages/flask/app.py", line 2292, in wsgi_app
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:     response = self.full_dispatch_request()
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:   File "/home/ctfd/.local/lib/python2.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:     rv = self.handle_user_exception(e)
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:   File "/home/ctfd/.local/lib/python2.7/site-packages/flask_restplus/api.py", line 583, in error_router
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:     return original_handler(e)
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:   File "/home/ctfd/.local/lib/python2.7/site-packages/flask/app.py", line 1718, in handle_user_exception
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:     reraise(exc_type, exc_value, tb)
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:   File "/home/ctfd/.local/lib/python2.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:     rv = self.dispatch_request()
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:   File "/home/ctfd/.local/lib/python2.7/site-packages/flask/app.py", line 1799, in dispatch_request
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:     return self.view_functions[rule.endpoint](**req.view_args)
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:   File "/home/ctfd/ctfd/CTFd/utils/decorators/visibility.py", line 81, in _check_registration_visibility
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:     return f(*args, **kwargs)
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:   File "/home/ctfd/ctfd/CTFd/utils/decorators/__init__.py", line 143, in ratelimit_function
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:     return f(*args, **kwargs)
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:   File "/home/ctfd/ctfd/CTFd/auth.py", line 198, in register
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:     email.verify_email_address(user.email)
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:   File "/home/ctfd/ctfd/CTFd/utils/email/__init__.py", line 39, in verify_email_address
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:     sendmail(addr, text)
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:   File "/home/ctfd/ctfd/CTFd/utils/email/__init__.py", line 15, in sendmail
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:     return smtp.sendmail(addr, text)
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:   File "/home/ctfd/ctfd/CTFd/utils/email/smtp.py", line 43, in sendmail
Dec 04 14:18:49 cat-dmz-1-web bash[21619]:     if auth:
Dec 04 14:18:49 cat-dmz-1-web bash[21619]: UnboundLocalError: local variable 'auth' referenced before assignment
Dec 04 14:18:49 cat-dmz-1-web bash[21619]: 192.168.2.1 - - [04/Dec/2018 14:18:49] "POST /register HTTP/1.1" 500 -
Dec 04 14:18:49 cat-dmz-1-web bash[21619]: 192.168.2.1 - - [04/Dec/2018 14:18:49] "GET /static/user.css HTTP/1.1" 200 -



Discussion:

Resolving patch:
diff --git a/CTFd/config.py b/CTFd/config.py
--- a/CTFd/config.py
+++ b/CTFd/config.py
@@ -117,10 +117,32 @@ class Config(object):
 
     MAIL_PORT:
         The mail port that emails are sent from if not overriden in the configuration panel.
+
+    MAIL_USEAUTH
+        Whether or not to use username and password to authenticate to the SMTP server
+
+    MAIL_USERNAME
+        The username used to authenticate to the SMTP server if MAIL_USEAUTH is defined
+
+    MAIL_PASSWORD
+        The password used to authenticate to the SMTP server if MAIL_USEAUTH is defined
+
+    MAIL_TLS
+        Whether to connect to the SMTP server over TLS
+
+    MAIL_SSL
+        Whether to connect to the SMTP server over SSL
+
+    MAILGUN_API_KEY
+        Mailgun API key to send email over Mailgun
+
+    MAILGUN_BASE_URL
+        Mailgun base url to send email over Mailgun
     '''
     MAILFROM_ADDR = os.getenv("MAILFROM_ADDR") or "noreply@ctfd.io"
     MAIL_SERVER = os.getenv("MAIL_SERVER") or None
     MAIL_PORT = os.getenv("MAIL_PORT")
+    MAIL_USEAUTH = os.getenv("MAIL_USEAUTH")
     MAIL_USERNAME = os.getenv("MAIL_USERNAME")
     MAIL_PASSWORD = os.getenv("MAIL_PASSWORD")
     MAIL_TLS = os.getenv("MAIL_TLS") or False
diff --git a/CTFd/utils/email/smtp.py b/CTFd/utils/email/smtp.py
--- a/CTFd/utils/email/smtp.py
+++ b/CTFd/utils/email/smtp.py
@@ -29,8 +29,7 @@ def sendmail(addr, text):
     password = get_config('mail_password') or get_app_config('MAIL_PASSWORD')
     TLS = get_config('mail_tls') or get_app_config('MAIL_TLS')
     SSL = get_config('mail_ssl') or get_app_config('MAIL_SSL')
-    if username:
-        auth = True
+    auth = get_config('mail_useauth') or get_app_config('MAIL_USEAUTH')
 
     if username:
         data['username'] = username


Test patch:
diff --git a/tests/users/test_auth.py b/tests/users/test_auth.py
--- a/tests/users/test_auth.py
+++ b/tests/users/test_auth.py
@@ -179,6 +179,7 @@ def test_expired_reset_password_link():
     with app.app_context():
         set_config('mail_server', 'localhost')
         set_config('mail_port', 25)
+        set_config('mail_useauth', True)
         set_config('mail_username', 'username')
         set_config('mail_password', 'password')
 
@@ -199,6 +200,7 @@ def test_invalid_reset_password_link():
     with app.app_context():
         set_config('mail_server', 'localhost')
         set_config('mail_port', 25)
+        set_config('mail_useauth', True)
         set_config('mail_username', 'username')
         set_config('mail_password', 'password')
 
@@ -237,6 +239,7 @@ def test_user_can_confirm_email(mock_smtp):
         set_config('verify_emails', True)
         set_config('mail_server', 'localhost')
         set_config('mail_port', 25)
+        set_config('mail_useauth', True)
         set_config('mail_username', 'username')
         set_config('mail_password', 'password')
 
@@ -279,6 +282,7 @@ def test_user_can_reset_password(mock_smtp):
         # Set CTFd to send emails
         set_config('mail_server', 'localhost')
         set_config('mail_port', 25)
+        set_config('mail_useauth', True)
         set_config('mail_username', 'username')
         set_config('mail_password', 'password')
 
diff --git a/tests/utils/test_email.py b/tests/utils/test_email.py
--- a/tests/utils/test_email.py
+++ b/tests/utils/test_email.py
@@ -34,6 +34,7 @@ def test_sendmail_with_smtp(mock_smtp):
     with app.app_context():
         set_config('mail_server', 'localhost')
         set_config('mail_port', 25)
+        set_config('mail_useauth', True)
         set_config('mail_username', 'username')
         set_config('mail_password', 'password')
 
@@ -143,6 +144,7 @@ def test_verify_email(mock_smtp):
     with app.app_context():
         set_config('mail_server', 'localhost')
         set_config('mail_port', 25)
+        set_config('mail_useauth', True)
         set_config('mail_username', 'username')
         set_config('mail_password', 'password')
         set_config('verify_emails', True)