Skip to content
Snippets Groups Projects

Přísnější kontroly mailových adres

Merged Martin Mareš requested to merge mj/check-email into devel
All threads resolved!
1 file
+ 47
2
Compare changes
  • Side-by-side
  • Inline
  • 64129d21
    Funkce email_check_domain je nyní přísnější:
    
      •  Odmítáme adresy, které mají display name. Není jasné, jestli
         nějaké takové byly předtím akceptované, protože jsme odmítali
         adresy s mezerami.
    
      •  S parametrem check_existence=True se ptáme DNS, zda doménová
         část adresy existuje. Vyžadujeme buď MX nebo A záznam. Dotazy
         nicméně mají krátký timeout (aby kontrola moc nebrzdila UI)
         a po něm adresu uznáme jako použitelnou.
    
      •  Domény "nomail" a "test" považujeme za korektní.
    
      •  Zaveden blacklist domén, zatím v něm je jenom obvyklý chyták
         gmail.cz. Možná ho chceme časem přesunout do konfigurace.
+ 47
2
@@ -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á
Loading