Seguridad: rate limiting, códigos de un solo uso, rotación de FERNET_KEY
- utils/rate_limiter.py: ventana deslizante 20 req/60s por chat_id, aplicado en todos los handlers de telegram_bot.py - database.py: tabla invite_codes (un solo uso por código), funciones crear_invite_code() y verificar_y_consumir_invite_code() - onboarding.py: verifica primero en DB (un solo uso), fallback al INVITE_CODE del .env (ilimitado, para uso interno) - scripts/generar_codigo.py: genera 1 o N códigos de invitación - scripts/rotar_fernet_key.py: re-cifra todos los tenants y el .env con nueva FERNET_KEY, backup automático de .env antes de escribir Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
67
scripts/generar_codigo.py
Normal file
67
scripts/generar_codigo.py
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
scripts/generar_codigo.py — Genera códigos de invitación de un solo uso.
|
||||
|
||||
Cada código solo puede ser usado por una persona para registrarse.
|
||||
Una vez consumido queda marcado en la DB y no se puede reutilizar.
|
||||
|
||||
Uso:
|
||||
python scripts/generar_codigo.py # genera uno aleatorio
|
||||
python scripts/generar_codigo.py --codigo abc123 # usa el código que vos elegís
|
||||
python scripts/generar_codigo.py --cantidad 5 # genera 5 códigos de una vez
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import secrets
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from config import get_settings
|
||||
from database import crear_invite_code, init_db
|
||||
|
||||
|
||||
def _generar() -> str:
|
||||
return secrets.token_urlsafe(6) # 8 chars URL-safe, 48 bits de entropía
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Genera códigos de invitación de un solo uso")
|
||||
parser.add_argument("--codigo", help="Código específico a registrar (default: aleatorio)")
|
||||
parser.add_argument("--cantidad", type=int, default=1, help="Cantidad de códigos a generar (default: 1)")
|
||||
args = parser.parse_args()
|
||||
|
||||
settings = get_settings()
|
||||
await init_db(settings.db_path)
|
||||
|
||||
codigos: list[str] = []
|
||||
|
||||
if args.codigo:
|
||||
codigos = [args.codigo]
|
||||
else:
|
||||
codigos = [_generar() for _ in range(args.cantidad)]
|
||||
|
||||
for codigo in codigos:
|
||||
await crear_invite_code(codigo)
|
||||
|
||||
print()
|
||||
if len(codigos) == 1:
|
||||
print(f" Código generado: {codigos[0]}")
|
||||
print()
|
||||
print(" Compartilo con el abogado que necesita registrarse.")
|
||||
print(" Solo puede ser usado una vez.")
|
||||
else:
|
||||
print(f" {len(codigos)} códigos generados (un solo uso cada uno):\n")
|
||||
for c in codigos:
|
||||
print(f" {c}")
|
||||
|
||||
print()
|
||||
print(" El código del .env (INVITE_CODE) sigue funcionando como acceso ilimitado.")
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user