fix: auth server-side para nuevo-invitado; sin hash en HTML
This commit is contained in:
@@ -7,6 +7,7 @@ No guarda los mensajes en disco: solo los reenvía por correo.
|
||||
"""
|
||||
import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
import hmac as _hmac
|
||||
import json
|
||||
import os
|
||||
from urllib.parse import urlparse, parse_qs, quote
|
||||
@@ -142,6 +143,15 @@ def invitado(n, f):
|
||||
return (n, ts) if -10 <= delta <= 14 else None
|
||||
|
||||
|
||||
def _gen_token(offset=0):
|
||||
"""Token HMAC diario para la página generadora. Válido 48h (slot actual + anterior)."""
|
||||
key = (os.environ.get('LINK_PASS') or '').encode()
|
||||
if not key:
|
||||
return None
|
||||
slot = str(int(time.time()) // 86400 - offset).encode()
|
||||
return _hmac.new(key, slot, 'sha256').hexdigest()
|
||||
|
||||
|
||||
def parse_dias(d_str):
|
||||
"""Valida una lista de timestamps separados por comas. Devuelve lista de ints o None."""
|
||||
if not d_str:
|
||||
@@ -182,7 +192,12 @@ class H(BaseHTTPRequestHandler):
|
||||
return (self.headers.get('X-Forwarded-For') or self.client_address[0]).split(',')[0].strip()
|
||||
|
||||
def do_GET(self):
|
||||
if urlparse(self.path).path != '/api/invitados/check':
|
||||
path = urlparse(self.path).path
|
||||
if path == '/api/gen-verify':
|
||||
tok = parse_qs(urlparse(self.path).query).get('t', [''])[0]
|
||||
valid = tok and tok in filter(None, [_gen_token(0), _gen_token(1)])
|
||||
return self.reply(200, {'ok': bool(valid)})
|
||||
if path != '/api/invitados/check':
|
||||
return self.reply(404, {'ok': False})
|
||||
if limited(check_hits, self.client_ip(), 40):
|
||||
return self.reply(429, {'ok': False})
|
||||
@@ -286,7 +301,20 @@ class H(BaseHTTPRequestHandler):
|
||||
self.reply(200, {'ok': True})
|
||||
|
||||
def do_POST(self):
|
||||
if self.path.split('?')[0] == '/api/invitados':
|
||||
p = self.path.split('?')[0]
|
||||
if p == '/api/gen-auth':
|
||||
try:
|
||||
n = int(self.headers.get('Content-Length', '0'))
|
||||
d = json.loads(self.rfile.read(n)) if 0 < n <= 1024 else {}
|
||||
except Exception:
|
||||
return self.reply(400, {'ok': False})
|
||||
link_pass = os.environ.get('LINK_PASS', '')
|
||||
if not link_pass:
|
||||
return self.reply(503, {'ok': False, 'error': 'No configurado.'})
|
||||
if _hmac.compare_digest(str(d.get('pass', '')), link_pass):
|
||||
return self.reply(200, {'ok': True, 'token': _gen_token(0)})
|
||||
return self.reply(401, {'ok': False, 'error': 'Contraseña incorrecta.'})
|
||||
if p == '/api/invitados':
|
||||
return self.invitados()
|
||||
if self.path not in ('/api/contacto', '/api/patrocinio'):
|
||||
return self.reply(404, {'ok': False})
|
||||
|
||||
Reference in New Issue
Block a user