Prevent traceback when using non-email in login

There's a traceback when the username used to log via SMTPAUTH
in is not an email address:

=== before ===
```
[...] ERROR in app: Exception on /internal/auth/email [GET]
Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py", line 1179, in _execute_context
    context = constructor(dialect, self, conn, *args)
  File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/default.py", line 719, in _init_compiled
    param.append(processors[key](compiled_params[key]))
  File "/usr/lib/python3.9/site-packages/sqlalchemy/sql/type_api.py", line 1201, in process
    return process_param(value, dialect)
  File "/app/mailu/models.py", line 60, in process_bind_param
    localpart, domain_name = value.lower().rsplit('@', 1)
ValueError: not enough values to unpack (expected 2, got 1)
[...]
[parameters: [{'%(140657157923216 param)s': 'foobar'}]]
```

=== after ===
```
[...] WARNING in nginx: Invalid user 'foobar': (builtins.ValueError) invalid email address (no "@")
```
master
Alexander Graf 3 years ago
parent 5236196288
commit 65133a960a

@ -5,6 +5,7 @@ import re
import urllib import urllib
import ipaddress import ipaddress
import socket import socket
import sqlalchemy.exc
import tenacity import tenacity
SUPPORTED_AUTH_METHODS = ["none", "plain"] SUPPORTED_AUTH_METHODS = ["none", "plain"]
@ -90,15 +91,19 @@ def handle_authentication(headers):
except: except:
app.logger.warn(f'Received undecodable user/password from nginx: {raw_user_email!r}/{raw_password!r}') app.logger.warn(f'Received undecodable user/password from nginx: {raw_user_email!r}/{raw_password!r}')
else: else:
user = models.User.query.get(user_email) try:
ip = urllib.parse.unquote(headers["Client-Ip"]) user = models.User.query.get(user_email)
if check_credentials(user, password, ip, protocol): except sqlalchemy.exc.StatementError as exc:
server, port = get_server(headers["Auth-Protocol"], True) exc = str(exc).split('\n', 1)[0]
return { app.logger.warn(f'Invalid user {user_email!r}: {exc}')
"Auth-Status": "OK", else:
"Auth-Server": server, if check_credentials(user, password, urllib.parse.unquote(headers["Client-Ip"]), protocol):
"Auth-Port": port server, port = get_server(headers["Auth-Protocol"], True)
} return {
"Auth-Status": "OK",
"Auth-Server": server,
"Auth-Port": port
}
status, code = get_status(protocol, "authentication") status, code = get_status(protocol, "authentication")
return { return {
"Auth-Status": status, "Auth-Status": status,

@ -57,6 +57,8 @@ class IdnaEmail(db.TypeDecorator):
def process_bind_param(self, value, dialect): def process_bind_param(self, value, dialect):
""" encode unicode domain part of email address to punycode """ """ encode unicode domain part of email address to punycode """
if not '@' in value:
raise ValueError('invalid email address (no "@")')
localpart, domain_name = value.lower().rsplit('@', 1) localpart, domain_name = value.lower().rsplit('@', 1)
if '@' in localpart: if '@' in localpart:
raise ValueError('email local part must not contain "@"') raise ValueError('email local part must not contain "@"')

Loading…
Cancel
Save