diff --git a/mo/users.py b/mo/users.py index b57c6e0557861c16c31f7ac9afb63089d2d94aeb..e765fe2f5366c20d0b778c54568b8aea8b9c44e1 100644 --- a/mo/users.py +++ b/mo/users.py @@ -3,6 +3,8 @@ import bcrypt import datetime import dateutil.tz +import dns.exception +import dns.resolver import email.errors import email.headerregistry import re @@ -246,7 +248,44 @@ def find_or_create_participation(user: db.User, contest: db.Contest, place: Opti return pion, is_new -def normalize_email(addr: str) -> str: +def email_is_fake(addr: str) -> bool: + return addr.endswith('@nomail') or addr.endswith('@test') + + +bad_domains = { + 'gmail.cz', +} + + +def email_check_domain(domain: str): + # Některé domény rovnou odmítáme + if domain in bad_domains: + logger.info(f'DNS: Doména <{domain}> na blacklistu') + raise mo.CheckError('Doména {domain} nepřijímá poštu') + + for record in ['MX', 'A', 'AAAA']: + try: + dns.resolver.resolve(domain, record, lifetime=2, search=False) + except dns.exception.Timeout: + # Kontrola je konzervativní, při timeoutu adresu raději schválíme + logger.info(f'DNS: Timeout při kontrole domény <{domain}>') + return + except dns.resolver.NoAnswer: + logger.debug(f'DNS: Doména <{domain}> neobsahuje {record}') + except dns.resolver.NXDOMAIN: + logger.info(f'DNS: Doména <{domain}> neexistuje') + raise mo.CheckError('Adresa obsahuje neexistující doménu {domain}') + except dns.exception.DNSException as e: + logger.warn(f'DNS: Záznam <{domain}>/{record} nejde resolvovat: {e}') + return + logger.debug(f'DNS: Doména <{domain}> OK') + return + + logger.info(f'DNS: Doména <{domain}> nepřijímá poštu') + raise mo.CheckError('Doména {domain} nepřijímá poštu') + + +def normalize_email(addr: str, check_existence: bool = False) -> str: if not re.fullmatch(r'.+@.+', addr): raise mo.CheckError('V e-mailové adrese chybí zavináč') @@ -263,10 +302,16 @@ def normalize_email(addr: str) -> str: try: # Tady úmyslně používáme knihovnu jen ke kontrole a ne k normalizaci, # protože nechceme riskovat, že se normalizovaný tvar časem změní. - email.headerregistry.Address(addr_spec=addr) + addr_obj = email.headerregistry.Address(addr_spec=addr) except (email.errors.HeaderParseError, ValueError): raise mo.CheckError('Chybná syntaxe mailové adresy') + if addr_obj.display_name != "": + raise mo.CheckError('Chybná syntaxe mailové adresy') + + if check_existence and not email_is_fake(addr): + email_check_domain(addr_obj.domain) + # XXX: Striktně vzato, tohle není korektní, protože některé domény mohou # mít case-sensitive levou stranu adresy. Ale i na nich se prakticky nevyskytují # levé strany s velkými písmeny, zatímco uživatelé při psaní adres běžně velká