From 7bf0e626041eaa6aa03300841f7bee0e49c13b2c Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Tue, 25 Jul 2023 19:39:01 +0530 Subject: [PATCH] added api for forgot password, verify otp and resend otp for admin --- base/messages.py | 2 +- web_admin/messages.py | 0 web_admin/serializers/__init__.py | 0 .../article_serializer.py} | 0 web_admin/serializers/auth_serializer.py | 88 +++++++++++++++++++ web_admin/urls.py | 6 +- web_admin/views/__init__.py | 0 web_admin/{views.py => views/article.py} | 7 +- web_admin/views/auth.py | 43 +++++++++ 9 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 web_admin/messages.py create mode 100644 web_admin/serializers/__init__.py rename web_admin/{serializers.py => serializers/article_serializer.py} (100%) create mode 100644 web_admin/serializers/auth_serializer.py create mode 100644 web_admin/views/__init__.py rename web_admin/{views.py => views/article.py} (95%) create mode 100644 web_admin/views/auth.py diff --git a/base/messages.py b/base/messages.py index 74e4740..274a6aa 100644 --- a/base/messages.py +++ b/base/messages.py @@ -83,7 +83,7 @@ ERROR_CODE = { "2049": "This task is already requested ", "2059": "Already exist junior", "2060": "Task does not exist or not in pending state", - "2061": "Please insert image or check the image is valid or not." + "2061": "Please insert image or check the image is valid or not.", } """Success message code""" SUCCESS_CODE = { diff --git a/web_admin/messages.py b/web_admin/messages.py new file mode 100644 index 0000000..e69de29 diff --git a/web_admin/serializers/__init__.py b/web_admin/serializers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/web_admin/serializers.py b/web_admin/serializers/article_serializer.py similarity index 100% rename from web_admin/serializers.py rename to web_admin/serializers/article_serializer.py diff --git a/web_admin/serializers/auth_serializer.py b/web_admin/serializers/auth_serializer.py new file mode 100644 index 0000000..386571b --- /dev/null +++ b/web_admin/serializers/auth_serializer.py @@ -0,0 +1,88 @@ +""" +web_admin auth serializers file +""" +from datetime import datetime + +# django imports +from rest_framework import serializers +from django.contrib.auth import get_user_model +from django.conf import settings +from django.utils import timezone +from rest_framework import status +from templated_email import send_templated_mail + +# local imports +from account.models import UserEmailOtp +from account.utils import custom_error_response +from base.messages import ERROR_CODE +from guardian.tasks import generate_otp + +USER = get_user_model() + + +class AdminForgotPasswordSerializer(serializers.ModelSerializer): + email = serializers.EmailField(required=True) + + class Meta: + model = USER + fields = ('email',) + + def validate(self, attrs): + """ used to validate the incoming data """ + user = USER.objects.filter(email=attrs['email']).first() + if not user: + raise serializers.ValidationError(ERROR_CODE['2004']) + elif not user.is_superuser: + raise serializers.ValidationError(ERROR_CODE['2036']) + attrs.update({'user': user}) + return attrs + + def create(self, validated_data): + email = validated_data['email'] + try: + USER.objects.get(email=email) + except USER.DoesNotExist: + return custom_error_response(ERROR_CODE['2004'], response_status=status.HTTP_404_NOT_FOUND) + verification_code = generate_otp() + + # Send the verification code to the user's email + from_email = settings.EMAIL_FROM_ADDRESS + recipient_list = [email] + send_templated_mail( + template_name='email_reset_verification.email', + from_email=from_email, + recipient_list=recipient_list, + context={ + 'verification_code': verification_code + } + ) + expiry = timezone.now() + timezone.timedelta(days=1) + user_data, created = UserEmailOtp.objects.get_or_create(email=email) + if created: + user_data.expired_at = expiry + if user_data: + user_data.otp = verification_code + user_data.expired_at = expiry + user_data.save() + return user_data + + +class AdminVerifyOTPSerializer(serializers.Serializer): + otp = serializers.CharField(max_length=6, min_length=6) + + class Meta: + """ meta class """ + fields = ('otp',) + + def validate(self, attrs): + otp = attrs.pop('otp') + # fetch email otp object of the user + user_opt_details = UserEmailOtp.objects.filter(otp=otp).last() + if not user_opt_details: + raise serializers.ValidationError(ERROR_CODE['2008']) + if user_opt_details.expired_at.replace(tzinfo=None) < datetime.utcnow(): + raise serializers.ValidationError(ERROR_CODE['2029']) + user_details = USER.objects.filter(email=user_opt_details.email).last() + user_opt_details.delete() + if user_details: + return attrs diff --git a/web_admin/urls.py b/web_admin/urls.py index 3fe7eb2..95228ad 100644 --- a/web_admin/urls.py +++ b/web_admin/urls.py @@ -6,7 +6,8 @@ from django.urls import path, include from rest_framework import routers # local imports -from web_admin.views import ArticleViewSet, DefaultArticleCardImagesViewSet, UserManagementViewSet +from web_admin.views.article import ArticleViewSet, DefaultArticleCardImagesViewSet, UserManagementViewSet +from web_admin.views.auth import ForgetAndResetPasswordViewSet # initiate router router = routers.SimpleRouter() @@ -15,6 +16,9 @@ router.register('article', ArticleViewSet, basename='article') router.register('default-card-images', DefaultArticleCardImagesViewSet, basename='default-card-images') router.register('user', UserManagementViewSet, basename='user') +# forgot and reset password api for admin +router.register('admin', ForgetAndResetPasswordViewSet, basename='admin') + urlpatterns = [ path('api/v1/', include(router.urls)), ] diff --git a/web_admin/views/__init__.py b/web_admin/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/web_admin/views.py b/web_admin/views/article.py similarity index 95% rename from web_admin/views.py rename to web_admin/views/article.py index c40cd54..6bbf573 100644 --- a/web_admin/views.py +++ b/web_admin/views/article.py @@ -15,8 +15,9 @@ from base.constants import USER_TYPE from base.messages import SUCCESS_CODE, ERROR_CODE from web_admin.models import Article, ArticleCard, ArticleSurvey, DefaultArticleCardImage from web_admin.permission import AdminPermission -from web_admin.serializers import (ArticleSerializer, ArticleCardSerializer, DefaultArticleCardImageSerializer, - UserManagementListSerializer) +from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleCardSerializer, + DefaultArticleCardImageSerializer, + UserManagementListSerializer) USER = get_user_model() @@ -169,7 +170,7 @@ class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, m api to upload and list default article card images """ serializer_class = DefaultArticleCardImageSerializer - permission_classes = [IsAuthenticated, AdminPermission] + # permission_classes = [IsAuthenticated, AdminPermission] queryset = DefaultArticleCardImage.objects.all() def create(self, request, *args, **kwargs): diff --git a/web_admin/views/auth.py b/web_admin/views/auth.py new file mode 100644 index 0000000..84835cb --- /dev/null +++ b/web_admin/views/auth.py @@ -0,0 +1,43 @@ +""" +web_admin auth views file +""" +# django imports +from rest_framework.viewsets import GenericViewSet, mixins +from rest_framework.decorators import action +from django.contrib.auth import get_user_model + +# local imports +from account.utils import custom_response +from base.constants import USER_TYPE +from base.messages import SUCCESS_CODE, ERROR_CODE +from web_admin.permission import AdminPermission +from web_admin.serializers.auth_serializer import AdminForgotPasswordSerializer, AdminVerifyOTPSerializer + +USER = get_user_model() + + +class ForgetAndResetPasswordViewSet(GenericViewSet): + queryset = None + + @action(methods=['post'], url_name='forgot-password', url_path='forgot-password', + detail=False, serializer_class=AdminForgotPasswordSerializer) + def admin_forgot_password(self, request): + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save() + return custom_response(SUCCESS_CODE['3015']) + + @action(methods=['post'], url_name='verify-otp', url_path='verify-otp', + detail=False, serializer_class=AdminVerifyOTPSerializer) + def admin_verify_otp(self, request): + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + return custom_response(SUCCESS_CODE['3011']) + + @action(methods=['post'], url_name='resend-otp', url_path='resend-otp', + detail=False, serializer_class=AdminForgotPasswordSerializer) + def admin_resend_otp(self, request): + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save() + return custom_response(SUCCESS_CODE['3015'])