Skip to content

Commit

Permalink
feat: remodelação do sistema de auth
Browse files Browse the repository at this point in the history
  • Loading branch information
davisilvarafacho committed Oct 9, 2024
1 parent 48dc9d2 commit e2e66ac
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 55 deletions.
181 changes: 174 additions & 7 deletions apps/users/serializers.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,57 @@
import uuid

from django.conf import settings
from django.contrib.auth import authenticate
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import update_last_login
from django.utils.module_loading import import_string
from django.contrib.auth.password_validation import validate_password
from django.core.cache import cache
from django.utils.translation import gettext_lazy as _
from django.utils.module_loading import import_string

from rest_framework import serializers
from rest_framework import exceptions

from rest_framework import exceptions, serializers
from rest_framework_simplejwt.serializers import (
PasswordField,
TokenObtainPairSerializer,
PasswordField,
)

from apps.system.core.classes import Email

from .models import Usuario


REDEFINIR_SENHA_CACHE_KEY = "reset-password-%s"

CONFIRMAR_EMAIL_CACHE_KEY = "email-confirm-%s"


def enviar_email_confirmacao_email(email):
codigo_confirmacao = str(uuid.uuid4())[:8].upper()
cache.set(CONFIRMAR_EMAIL_CACHE_KEY % email, codigo_confirmacao)

email = Email(
titulo="Confirmação de Email",
corpo="Esse é o código de confirmação: %s" % codigo_confirmacao,
destinatarios=[email],
)

email.send()


class UsuarioSerializer(serializers.ModelSerializer):
password = PasswordField()

def validate_password(self, password):
validate_password(password)
return make_password(password)

def create(self, validated_data):
instance = super().create(validated_data)
enviar_email_confirmacao_email(instance.email)
return instance

class Meta:
model = Usuario
exclude = (
Expand All @@ -37,6 +73,132 @@ class Meta:
)


class ReenviaEmailConfirmacaoSerializer(serializers.Serializer):
usuario = serializers.PrimaryKeyRelatedField(queryset=Usuario.objects.all())

def validate_usuario(self, value):
if value.email_verified:
raise serializers.ValidationError(
{"usuario": "Esse usuário já possui o email confirmado"}
)
return value

def save(self):
enviar_email_confirmacao_email(self.validated_data["usuario"].email)


class ConfirmarEmailSerializer(serializers.Serializer):
usuario = serializers.PrimaryKeyRelatedField(queryset=Usuario.objects.all())
codigo = serializers.CharField()

def validate(self, attrs):
validated_data = super().validate(attrs)

usuario = validated_data["usuario"]

if usuario.email_verified:
raise serializers.ValidationError(
{"mensagem": _("O usuário já confirmou o email")},
code="invalid_request",
)

codigo = validated_data["codigo"]

cache_key = CONFIRMAR_EMAIL_CACHE_KEY % usuario.email
codigo_cache = cache.get(cache_key, None)
if codigo_cache is None:
raise serializers.ValidationError(
{"mensagem": _("O código de confirmação expirou")}, code="expired_code"
)

if codigo != codigo_cache:
raise serializers.ValidationError(
{"mensagem": _("O código informado não é inválido")}
)

cache.delete(cache_key)

return validated_data

def save(self):
validated_data = self.validated_data
usuario = validated_data["usuario"]
usuario.email_confirmed = True
usuario.save()


class EnviarEmailRedefinicaoSenhaSerializer(serializers.Serializer):
usuario = serializers.SlugRelatedField(
queryset=Usuario.objects.all(),
slug_field="email",
)

def save(self):
usuario = self.validated_data["usuario"]

cache_key = REDEFINIR_SENHA_CACHE_KEY % usuario.email

codigo = uuid.uuid4().hex[:8].upper()
cache.set(cache_key, codigo, 60 * 10)

email = Email(
titulo="Redefinição de senha",
corpo="Esse é o código da redefinição de senha: %s" % codigo,
destinatarios=[usuario.email],
)

email.send()


class ConfirmarCodigoRedefinirSenhaSerializer(serializers.Serializer):
usuario = serializers.SlugRelatedField(
queryset=Usuario.objects.all(), slug_field="email"
)
codigo = serializers.CharField()

def validate(self, attrs):
dados = super().validate(attrs)

codigo = dados["codigo"]
usuario = dados["usuario"]

cache_key = REDEFINIR_SENHA_CACHE_KEY % usuario.email
codigo_cache = cache.get(cache_key, None)

if codigo_cache is None:
raise serializers.ValidationError(
{"mensagem": _("A redefinição de senha expirou")}
)

if codigo != codigo_cache:
raise serializers.ValidationError(
{"mensagem": _("O código informado é inválido")}
)

cache.delete(cache_key)

return dados


class RedefinirSenhaSerializer(serializers.Serializer):
usuario = serializers.SlugRelatedField(
queryset=Usuario.objects.all(), slug_field="email"
)
nova_senha = serializers.CharField()

def validate_nova_senha(self, value):
validate_password(value)
return value

def save(self):
validated_data = self.validated_data
nova_senha = validated_data["nova_senha"]
usuario = validated_data["usuario"]

usuario.password = make_password(nova_senha)
usuario.save()


class LoginSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
authenticate_kwargs = {
Expand All @@ -52,6 +214,7 @@ def validate(self, attrs):
usuario = Usuario.objects.filter(
email=authenticate_kwargs[self.username_field]
).first()

if not usuario:
raise exceptions.AuthenticationFailed(
{
Expand All @@ -73,22 +236,26 @@ def validate(self, attrs):
authentication_rule = import_string(
settings.SIMPLE_JWT["USER_AUTHENTICATION_RULE"]
)

if not authentication_rule(self.user):
raise exceptions.AuthenticationFailed(
{"mensagem": _("A senha informada está incorreta")},
"incorret_password",
)

data = super().validate(attrs)
refresh = self.get_token(self.user)
data["access"] = str(refresh.access_token)
data = {}
token = self.get_token(self.user)
data["access"] = str(token.access_token)

if settings.SIMPLE_JWT["UPDATE_LAST_LOGIN"]:
update_last_login(None, self.user)

return data

@classmethod
def get_token(cls, user):
def get_token(cls, user): # , assinatura
token = super().get_token(user)
token["user_name"] = user.first_name
token["user_last_name"] = user.last_name
token["user_full_name"] = user.get_full_name()
return token
12 changes: 5 additions & 7 deletions apps/users/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@

from rest_framework.routers import DefaultRouter

from rest_framework_simplejwt.views import token_obtain_pair as login_view
from .views import LoginViewSet, AuthViewSet

from .views import UsuarioViewSet

router_v1 = DefaultRouter()
router_v1.register("usuarios", UsuarioViewSet, "usuarios")
router_auth = DefaultRouter()
router_auth.register("", AuthViewSet, "auth")

urlpatterns = [
path("auth/login/", login_view),
path("v1/", include(router_v1.urls)),
path("auth/", include(router_auth.urls)),
path("auth/token/obtain/", LoginViewSet.as_view()),
]
114 changes: 73 additions & 41 deletions apps/users/views.py
Original file line number Diff line number Diff line change
@@ -1,55 +1,87 @@
from django.utils.translation import gettext_lazy as _

from rest_framework import status
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet

from apps.system.core.classes import Email
from rest_framework_simplejwt.views import TokenObtainPairView

from apps.system.base.views import BaseViewSet

from .serializers import (
Usuario,
UsuarioSerializer,
ReenviaEmailConfirmacaoSerializer,
ConfirmarEmailSerializer,
EnviarEmailRedefinicaoSenhaSerializer,
ConfirmarCodigoRedefinirSenhaSerializer,
RedefinirSenhaSerializer,
)

from .serializers import Usuario, UsuarioSerializer

class LoginViewSet(TokenObtainPairView):
authentication_classes = []
permission_classes = [AllowAny]

class UsuarioViewSet(ModelViewSet):
queryset = Usuario.objects.all()

class AuthViewSet(BaseViewSet):
serializer_class = UsuarioSerializer
authentication_classes = []
permission_classes = [AllowAny]

@action(methods=["get"], detail=False)
def validar_cadastro_email(self, request):
email = request.query_params.get("email", None)
if email is None:
raise ValidationError({"email": "Essa query é obrigatória"})

def perform_create(self, serializer):
serializer.save(is_active=False)
self.enviar_email_confirmacao(serializer.instance.email)

def enviar_email_confirmacao(self, email):
email = Email(_("Confirme seu email"), corpo="Clique no link abaixo para confirmar sua conta", destinatarios=[email])
email.send()

@action(methods=['get'], detail=False)
def verificar_cadastro_email(self, request, pk):
email_usuario = pk # estou passando o email do usuário no lugar da pk
try:
Usuario.objects.get(email=email_usuario)
return Response()
Usuario.objects.get(email=email)
return Response({"cadastrado": True})
except Usuario.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

@action(methods=['post'], detail=True)
def confirmar_email(self, request, pk):
instance = self.get_object()
if instance.is_active:
return Response({
"mensagem": _("Esse usuário já está ativo")
}, status=status.HTTP_400_BAD_REQUEST)

instance.is_active = True
instance.save()
return Response({"cadastrado": False})

@action(methods=["post"], detail=False)
def cadastro(self, request):
serializer = UsuarioSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)

@action(methods=["post"], detail=False)
def confirmar_email(self, request):
serializer = ConfirmarEmailSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(status=status.HTTP_204_NO_CONTENT)

@action(methods=["post"], detail=False)
def reenviar_email_confirmacao(self, request):
serializer = ReenviaEmailConfirmacaoSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(status=status.HTTP_204_NO_CONTENT)

@action(methods=["post"], detail=False)
def enviar_email_redefinicao_senha(self, request):
serializer = EnviarEmailRedefinicaoSenhaSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response()

@action(methods=['post'], detail=True)
def reenviar_email(self, request, pk):
instance = self.get_object()
if instance.is_active:
return Response({
"mensagem": _("Esse usuário já está ativo")
}, status=status.HTTP_400_BAD_REQUEST)
@action(methods=["post"], detail=False)
def confirmar_codigo_redefinir_senha(self, request):
serializer = ConfirmarCodigoRedefinirSenhaSerializer(
data=request.data, context={"request": self.request}
)
serializer.is_valid(raise_exception=True)
return Response()

self.enviar_email_confirmacao(instance.email)
return Response()
@action(methods=["post"], detail=False)
def redefinir_senha(self, request):
serializer = RedefinirSenhaSerializer(
data=request.data, context={"request": self.request}
)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response()

0 comments on commit e2e66ac

Please sign in to comment.