Tesis 1.0.0
Loading...
Searching...
No Matches
views.py
Go to the documentation of this file.
1from django.shortcuts import render
2from rest_framework.views import APIView
3from rest_framework.response import Response
4from rest_framework import status
5from .models import User2FA
6from .serializers import Setup2FASerializer, Verify2FASerializer, AuthorizePurchaseSerializer
7import pyotp
8import qrcode
9import base64
10from io import BytesIO
11import logging
12
13logger = logging.getLogger(__name__)
14
15# Añadimos una clase para comprobar el estado del servicio
16class HealthCheckView(APIView):
17 def get(self, request):
18 return Response({"status": "ok", "service": "2FA Backend", "version": "1.0"})
19
20 def post(self, request):
21 return Response({
22 "status": "ok",
23 "service": "2FA Backend",
24 "version": "1.0",
25 "received_data": request.data
26 })
27
28class Setup2FAView(APIView):
29 def post(self, request):
30 email = request.data.get('email')
31 if not email:
32 return Response({'error': 'Email requerido'}, status=400)
33 user2fa, created = User2FA.objects.get_or_create(email=email)
34 if not user2fa.secret:
35 user2fa.secret = pyotp.random_base32()
36 user2fa.save()
37 totp = pyotp.TOTP(user2fa.secret)
38 uri = totp.provisioning_uri(name=email, issuer_name="Back2FA")
39 qr = qrcode.make(uri)
40 buffer = BytesIO()
41 qr.save(buffer, format="PNG")
42 img_str = base64.b64encode(buffer.getvalue()).decode()
43 return Response({
44 'email': email,
45 'secret': user2fa.secret,
46 'qr': f"data:image/png;base64,{img_str}"
47 })
48
49class Verify2FAView(APIView):
50 def post(self, request):
51 serializer = Verify2FASerializer(data=request.data)
52 serializer.is_valid(raise_exception=True)
53 email = serializer.validated_data['email']
54 code = serializer.validated_data['code']
55 try:
56 user2fa = User2FA.objects.get(email=email)
57 except User2FA.DoesNotExist:
58 return Response({'error': 'Usuario no tiene 2FA configurado'}, status=404)
59 totp = pyotp.TOTP(user2fa.secret)
60 if totp.verify(code):
61 user2fa.enabled = True
62 user2fa.save()
63 return Response({'verified': True})
64 return Response({'verified': False}, status=400)
65
66class AuthorizePurchaseView(APIView):
67 def post(self, request):
68 logger.info(f"POST /api/2fa/authorize/ - data: {request.data}")
69 try:
70 serializer = AuthorizePurchaseSerializer(data=request.data)
71
72 # Validación más detallada para capturar errores específicos
73 if not serializer.is_valid():
74 logger.error(f"Error de validación: {serializer.errors}")
75 return Response({'error': 'Datos inválidos', 'detalles': serializer.errors}, status=400)
76
77 email = serializer.validated_data['email']
78 monto = serializer.validated_data['monto']
79 titular = serializer.validated_data['titular']
80 code = serializer.validated_data.get('code')
81
82 logger.info(f"AuthorizePurchase: email={email}, monto={monto}, titular={titular}, code={code}")
83
84 # Verificar si el usuario existe o crear automáticamente
85 user2fa, created = User2FA.objects.get_or_create(email=email)
86 if created:
87 logger.info(f"Se creó un nuevo usuario 2FA: {email}")
88 user2fa.secret = pyotp.random_base32()
89 user2fa.save()
90
91 requiere_2fa = False
92 motivo = []
93
94 if monto > 50000:
95 requiere_2fa = True
96 motivo.append('monto')
97
98 # Suponiendo que el nombre del usuario está en el email (ajustar según tu lógica real)
99 if titular.strip().lower() not in email.strip().lower():
100 requiere_2fa = True
101 motivo.append('titular')
102
103 if requiere_2fa:
104 if not user2fa.enabled:
105 logger.info(f"2FA requerido pero no configurado para {email}")
106 return Response({'requiere_2fa': True, 'motivo': motivo, 'configurado': False}, status=200)
107
108 if not code:
109 logger.info(f"2FA requerido, esperando código para {email}")
110 return Response({'requiere_2fa': True, 'motivo': motivo, 'configurado': True}, status=200)
111
112 totp = pyotp.TOTP(user2fa.secret)
113 if not totp.verify(code):
114 logger.warning(f"Código 2FA inválido para {email}")
115 return Response({'error': 'Código 2FA inválido'}, status=400)
116
117 logger.info(f"Compra autorizada con 2FA para {email}")
118 return Response({'autorizado': True, 'motivo': motivo})
119
120 logger.info(f"Compra autorizada sin 2FA para {email}")
121 return Response({'autorizado': True, 'motivo': motivo})
122
123 except Exception as e:
124 logger.error(f"Error inesperado: {str(e)}", exc_info=True)
125 return Response({'error': 'Error interno del servidor'}, status=500)
post(self, request)
Definition views.py:20
get(self, request)
Definition views.py:17
post(self, request)
Definition views.py:29
post(self, request)
Definition views.py:50