From 7a9034943a5e00cc6cba89be6305b3597eb4ce30 Mon Sep 17 00:00:00 2001 From: Mono Mono Date: Sat, 14 Feb 2026 15:12:32 -0500 Subject: [PATCH] feat(API): change to jwt authentication. --- requirements.txt | 1 + tienda_ilusion/don_confiao/tests/Mixins.py | 19 +++++-- tienda_ilusion/requirements.txt | 5 ++ tienda_ilusion/tienda_ilusion/settings.py | 16 ++++-- .../templates/registration/login.html | 5 -- .../templates/registration/profile.html | 27 ---------- .../tienda_ilusion/templates/start.html | 22 -------- tienda_ilusion/tienda_ilusion/urls.py | 14 +++-- tienda_ilusion/tienda_ilusion/views.py | 15 ------ tienda_ilusion/users/__init__.py | 0 tienda_ilusion/users/admin.py | 3 ++ tienda_ilusion/users/apps.py | 5 ++ tienda_ilusion/users/migrations/__init__.py | 0 tienda_ilusion/users/models.py | 3 ++ tienda_ilusion/users/serializers.py | 8 +++ tienda_ilusion/users/tests.py | 51 +++++++++++++++++++ tienda_ilusion/users/urls.py | 6 +++ tienda_ilusion/users/views.py | 12 +++++ 18 files changed, 130 insertions(+), 82 deletions(-) create mode 100644 tienda_ilusion/requirements.txt delete mode 100644 tienda_ilusion/tienda_ilusion/templates/registration/login.html delete mode 100644 tienda_ilusion/tienda_ilusion/templates/registration/profile.html delete mode 100644 tienda_ilusion/tienda_ilusion/templates/start.html delete mode 100644 tienda_ilusion/tienda_ilusion/views.py create mode 100644 tienda_ilusion/users/__init__.py create mode 100644 tienda_ilusion/users/admin.py create mode 100644 tienda_ilusion/users/apps.py create mode 100644 tienda_ilusion/users/migrations/__init__.py create mode 100644 tienda_ilusion/users/models.py create mode 100644 tienda_ilusion/users/serializers.py create mode 100644 tienda_ilusion/users/tests.py create mode 100644 tienda_ilusion/users/urls.py create mode 100644 tienda_ilusion/users/views.py diff --git a/requirements.txt b/requirements.txt index 360a414..1926c0e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ Django==5.0.6 djangorestframework django-cors-headers +djangorestframework-simplejwt sabatron-tryton-rpc-client==7.4.0 diff --git a/tienda_ilusion/don_confiao/tests/Mixins.py b/tienda_ilusion/don_confiao/tests/Mixins.py index 7957827..37176d9 100644 --- a/tienda_ilusion/don_confiao/tests/Mixins.py +++ b/tienda_ilusion/don_confiao/tests/Mixins.py @@ -1,10 +1,19 @@ from django.contrib.auth.models import User +from rest_framework_simplejwt.tokens import RefreshToken +from rest_framework.test import APIClient class LoginMixin: def login(self): - username = 'nombre_usuario' - password = 'contraseña' - email = 'correo@example.com' - self.user = User.objects.create_user(username, email, password) - self.client.login(username=username, password=password) + self.user = User.objects.create_superuser( + username='admin', + email='admin@example.com', + password='adminpass' + ) + + refresh = RefreshToken.for_user(self.user) + self.access_token = str(refresh.access_token) + + self.client = APIClient() + self.client.credentials( + HTTP_AUTHORIZATION=f'Bearer {self.access_token}') diff --git a/tienda_ilusion/requirements.txt b/tienda_ilusion/requirements.txt new file mode 100644 index 0000000..1926c0e --- /dev/null +++ b/tienda_ilusion/requirements.txt @@ -0,0 +1,5 @@ +Django==5.0.6 +djangorestframework +django-cors-headers +djangorestframework-simplejwt +sabatron-tryton-rpc-client==7.4.0 diff --git a/tienda_ilusion/tienda_ilusion/settings.py b/tienda_ilusion/tienda_ilusion/settings.py index df1a981..6678e0b 100644 --- a/tienda_ilusion/tienda_ilusion/settings.py +++ b/tienda_ilusion/tienda_ilusion/settings.py @@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/5.0/ref/settings/ """ import os +from datetime import timedelta from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -44,7 +45,9 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', + 'rest_framework.authtoken', 'corsheaders', + 'users', # 'don_confiao' ] @@ -57,7 +60,6 @@ MIDDLEWARE = [ 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'corsheaders.middleware.CorsMiddleware', - 'django.middleware.common.CommonMiddleware', ] ROOT_URLCONF = 'tienda_ilusion.urls' @@ -137,11 +139,19 @@ FIXTURE_DIRS = ['don_confiao/tests/Fixtures'] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ - 'rest_framework.authentication.SessionAuthentication', + "rest_framework_simplejwt.authentication.JWTAuthentication", ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ], } -LOGOUT_REDIRECT_URL = '/start/' +SIMPLE_JWT = { + "ACCESS_TOKEN_LIFETIME": timedelta(minutes=30), + "REFRESH_TOKEN_LIFETIME": timedelta(days=1), + "AUTH_HEADER_TYPES": ("Bearer",), +} + +# CORS_ALLOWED_ORIGINS = [ +# "http://localhost:5173", +# ] diff --git a/tienda_ilusion/tienda_ilusion/templates/registration/login.html b/tienda_ilusion/tienda_ilusion/templates/registration/login.html deleted file mode 100644 index 0ec0e16..0000000 --- a/tienda_ilusion/tienda_ilusion/templates/registration/login.html +++ /dev/null @@ -1,5 +0,0 @@ -
- {% csrf_token %} - {{ form.as_p }} - -
diff --git a/tienda_ilusion/tienda_ilusion/templates/registration/profile.html b/tienda_ilusion/tienda_ilusion/templates/registration/profile.html deleted file mode 100644 index e70c7fe..0000000 --- a/tienda_ilusion/tienda_ilusion/templates/registration/profile.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - Perfil de usuario - - - - -
-
-
-

Perfil de usuario

-

Nombre de usuario: {{ user.username }}

-

Email: {{ user.email }}

-
- {% csrf_token %} - -
-
-
-
- - diff --git a/tienda_ilusion/tienda_ilusion/templates/start.html b/tienda_ilusion/tienda_ilusion/templates/start.html deleted file mode 100644 index 4ebf3bd..0000000 --- a/tienda_ilusion/tienda_ilusion/templates/start.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - Bienvenido a la tienda la ilusión - - - -
-
-
-

Bienvenido a la tienda la ilusion

- - Login -
-
-
- - diff --git a/tienda_ilusion/tienda_ilusion/urls.py b/tienda_ilusion/tienda_ilusion/urls.py index 213403f..602cc8b 100644 --- a/tienda_ilusion/tienda_ilusion/urls.py +++ b/tienda_ilusion/tienda_ilusion/urls.py @@ -16,15 +16,19 @@ Including another URLconf """ from django.contrib import admin from django.urls import include, path -from . import views - +from rest_framework_simplejwt.views import ( + TokenObtainPairView, + TokenRefreshView, +) app_name = "don_confiao" urlpatterns = [ path("don_confiao/", include("don_confiao.urls")), - path("accounts/", include("django.contrib.auth.urls")), - path('accounts/profile/', views.ProfileView.as_view(), name='profile'), - path('start/', views.StartView.as_view(), name='start'), path('admin/', admin.site.urls), + path('api/token/', TokenObtainPairView.as_view(), + name='token_obtain_pair'), + path('api/token/refresh/', TokenRefreshView.as_view(), + name='token_refresh'), + path('api/users/', include('users.urls')), ] diff --git a/tienda_ilusion/tienda_ilusion/views.py b/tienda_ilusion/tienda_ilusion/views.py deleted file mode 100644 index ade2409..0000000 --- a/tienda_ilusion/tienda_ilusion/views.py +++ /dev/null @@ -1,15 +0,0 @@ -from django.views.generic import TemplateView -from django.contrib.auth.mixins import LoginRequiredMixin - - -class ProfileView(LoginRequiredMixin, TemplateView): - template_name = 'registration/profile.html' - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context['user'] = self.request.user - return context - - -class StartView(TemplateView): - template_name = 'start.html' diff --git a/tienda_ilusion/users/__init__.py b/tienda_ilusion/users/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tienda_ilusion/users/admin.py b/tienda_ilusion/users/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/tienda_ilusion/users/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/tienda_ilusion/users/apps.py b/tienda_ilusion/users/apps.py new file mode 100644 index 0000000..4ce1fab --- /dev/null +++ b/tienda_ilusion/users/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class UsersConfig(AppConfig): + name = 'users' diff --git a/tienda_ilusion/users/migrations/__init__.py b/tienda_ilusion/users/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tienda_ilusion/users/models.py b/tienda_ilusion/users/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/tienda_ilusion/users/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/tienda_ilusion/users/serializers.py b/tienda_ilusion/users/serializers.py new file mode 100644 index 0000000..cdc3387 --- /dev/null +++ b/tienda_ilusion/users/serializers.py @@ -0,0 +1,8 @@ +from django.contrib.auth.models import User +from rest_framework import serializers + + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ('id', 'username', 'email', 'first_name', 'last_name') diff --git a/tienda_ilusion/users/tests.py b/tienda_ilusion/users/tests.py new file mode 100644 index 0000000..ad750ad --- /dev/null +++ b/tienda_ilusion/users/tests.py @@ -0,0 +1,51 @@ +from django.test import TestCase +from django.urls import reverse +from django.contrib.auth.models import User +from rest_framework.test import APIClient +from rest_framework_simplejwt.tokens import RefreshToken + + +class MeEndpointTests(TestCase): + def setUp(self): + self.user = User.objects.create_superuser( + username='admin', + email='admin@example.com', + password='adminpass' + ) + + refresh = RefreshToken.for_user(self.user) + self.access_token = str(refresh.access_token) + + self.client = APIClient() + self.client.credentials( + HTTP_AUTHORIZATION=f'Bearer {self.access_token}') + + def test_me_endpoint_returns_correct_user_data(self): + """ + Verifica que GET /api/users/me/ devuelve los datos del usuario + autenticado. + """ + url = reverse('current-user') + response = self.client.get(url) + + self.assertEqual(response.status_code, 200) + + expected_fields = {'id', 'username', 'email', + 'first_name', 'last_name'} + self.assertTrue(expected_fields.issubset(response.json().keys())) + + data = response.json() + self.assertEqual(data['username'], self.user.username) + self.assertEqual(data['email'], self.user.email) + self.assertEqual(data['first_name'], self.user.first_name) + self.assertEqual(data['last_name'], self.user.last_name) + + def test_me_endpoint_requires_authentication(self): + """ + Sin token el endpoint debe devolver 401 Unauthorized. + """ + client_no_auth = APIClient() + url = reverse('current-user') + response = client_no_auth.get(url) + + self.assertEqual(response.status_code, 401) diff --git a/tienda_ilusion/users/urls.py b/tienda_ilusion/users/urls.py new file mode 100644 index 0000000..8d408f9 --- /dev/null +++ b/tienda_ilusion/users/urls.py @@ -0,0 +1,6 @@ +from django.urls import path +from .views import CurrentUserView + +urlpatterns = [ + path('me/', CurrentUserView.as_view(), name='current-user'), +] diff --git a/tienda_ilusion/users/views.py b/tienda_ilusion/users/views.py new file mode 100644 index 0000000..68762b8 --- /dev/null +++ b/tienda_ilusion/users/views.py @@ -0,0 +1,12 @@ +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated +from .serializers import UserSerializer + + +class CurrentUserView(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request): + serializer = UserSerializer(request.user) + return Response(serializer.data)