8920585b24
recover_account_view era un stub que solo renderizaba (los formularios no tenían backend y el HTML arrastraba un reCAPTCHA de otro dominio). Ahora es funcional: Backend: - Modelo PasswordReset (token+email+expiración 1h) + migración 0014. - recover_account_view maneja POST JSON con 3 tipos, todo por email (bnet), con respuestas genéricas anti-enumeración: * password: crea token y envía enlace de reset (emails/password_reset.html). * accountname: envía las cuentas de juego ligadas (emails/account_names.html). * activation: reenvía el enlace de activación pendiente (emails/activation.html). - reset_password_view: GET valida el token (isla), POST re-deriva el verifier SRP6 v2 de la cuenta bnet (bnet.bnet_make_registration) y lo actualiza; marca el token usado. Ruta 'reset-password'. - Resiliente si la BD de cuentas (AzerothCore) no está disponible: mensaje limpio, nunca 500. Frontend (islas Vite): RecoverForm.tsx (selector de tipo + email) y ResetPassword.tsx (nueva contraseña con token). Se elimina el reCAPTCHA roto; se puede añadir uno propio si se aportan claves. Verificado en producción: los 3 flujos devuelven éxito genérico; reset valida token y contraseñas; token válido no provoca 500. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
70 lines
2.8 KiB
Python
70 lines
2.8 KiB
Python
"""Imports y utilidades compartidas por el paquete de vistas (`home.views`).
|
|
|
|
Cada submódulo de vistas hace `from ._base import *` para heredar estos imports
|
|
y el decorador `require_paid_stripe`.
|
|
"""
|
|
import socket, hashlib, gmpy2, binascii, os, re, json, logging, secrets
|
|
from collections import Counter
|
|
from datetime import datetime, timedelta
|
|
from decimal import Decimal, ROUND_HALF_UP
|
|
from functools import wraps
|
|
|
|
from django import forms
|
|
from django.conf import settings
|
|
from django.contrib import messages
|
|
from django.contrib.auth import logout
|
|
from django.db import connections
|
|
from django.http import HttpResponse, JsonResponse
|
|
from django.shortcuts import render, redirect
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
from django.utils.crypto import get_random_string
|
|
from django.utils.timezone import now
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
import stripe
|
|
|
|
from ..ac_soap import execute_soap_command
|
|
from ..library_correo import enviar_correo
|
|
from ..pagos_stripe import create_checkout_session, is_session_paid
|
|
from ..pagos_sumup import crear_checkout, crear_checkout_battlepay, obtener_checkout
|
|
from ..zone_definitions import get_zone_name
|
|
from .. import bnet
|
|
from ..models import (
|
|
Noticia, ClienteCategoria, ServerSelection, RecruitAFriend, DownloadClientPage,
|
|
ContentCreator, RecruitReward, ClaimedReward, AccountActivation, SecurityToken,
|
|
GuildRenameSettings, VoteSite, VoteLog, HomeApiPoints, UnstuckHistory, ReviveHistory,
|
|
RenamePrice, CustomizePrice, ChangeRacePrice, ChangeFactionPrice, LevelUpPrice,
|
|
GoldPrice, TransferPrice, Category, Item, StripeLog, LoginAttempt, Pedido,
|
|
PasswordReset,
|
|
)
|
|
|
|
# Configuración del logger
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def require_paid_stripe(redirect_to='index'):
|
|
"""
|
|
Protege las vistas de "éxito" de pago: exige un `session_id` de Stripe
|
|
realmente PAGADO en la URL (Stripe lo añade tras el pago) y evita re-entregas
|
|
marcando el StripeLog como `fulfilled`. Cierra el bypass de ir directo a la
|
|
success-url sin pagar.
|
|
"""
|
|
def deco(view):
|
|
@wraps(view)
|
|
def wrapper(request, *args, **kwargs):
|
|
session_id = request.GET.get('session_id')
|
|
if not is_session_paid(session_id):
|
|
messages.error(request, 'No se ha podido verificar el pago.')
|
|
return redirect(redirect_to)
|
|
log = StripeLog.objects.filter(session_id=session_id).first()
|
|
if log is not None and log.fulfilled:
|
|
messages.info(request, 'Este pago ya había sido procesado.')
|
|
return redirect(redirect_to)
|
|
response = view(request, *args, **kwargs)
|
|
if log is not None:
|
|
log.fulfilled = True
|
|
log.save(update_fields=['fulfilled'])
|
|
return response
|
|
return wrapper
|
|
return deco
|