From 0a35f06ff51588da011c1340d954c05d5b9c85dd Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 12 Aug 2019 15:06:38 +0300 Subject: [PATCH] version 0.0.5.11: refactored and setup CORS --- apps/account/models.py | 20 ++++++++---- apps/account/serializers/common.py | 2 +- apps/account/serializers/web.py | 50 ++++++++++++++++++++++++------ apps/account/urls/web.py | 8 ++--- apps/account/views/web.py | 43 +++++++++++-------------- project/settings/base.py | 5 +++ requirements/base.txt | 5 ++- requirements/development.txt | 2 ++ 8 files changed, 87 insertions(+), 48 deletions(-) diff --git a/apps/account/models.py b/apps/account/models.py index 6a54d8f4..e5d1b362 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -1,3 +1,4 @@ +"""Account models""" from typing import Union from django.conf import settings @@ -79,7 +80,9 @@ class User(ImageMixin, AbstractUser): """Method to remove user refresh tokens""" source = source if isinstance(source, list) else [source, ] refresh_tokens = self.oauth2_provider_refreshtoken.filter( - application__source__in=source) + application__source__in=source, + access_token__isnull=False + ) if refresh_tokens.exists(): for token in refresh_tokens: token.revoke() @@ -156,10 +159,15 @@ class ResetPasswordToken(PlatformMixin, ProjectBaseMixin): """Check if valid token or not""" return timezone.now() > self.expiry_datetime - def generate_key(self): - """generates a pseudo random code""" + def generate_token(self): + """Generates a pseudo random code""" return default_token_generator.make_token(self.user) + @staticmethod + def token_is_valid(user, token): + """Check if token is valid""" + return default_token_generator.check_token(user, token) + def save(self, *args, **kwargs): """Override save method""" if not self.expiry_datetime: @@ -168,7 +176,7 @@ class ResetPasswordToken(PlatformMixin, ProjectBaseMixin): timezone.timedelta(hours=self.get_resetting_token_expiration) ) if not self.key: - self.key = self.generate_key() + self.key = self.generate_token() return super(ResetPasswordToken, self).save(*args, **kwargs) def get_reset_password_template(self): @@ -186,7 +194,7 @@ class ResetPasswordToken(PlatformMixin, ProjectBaseMixin): message=self.get_reset_password_template()) def confirm_reset_password_request(self): - """Method to confirm reset user passwrod request""" + """Method to confirm reset user password request""" # Remove access token and revoke refresh tokens self.user.remove_access_tokens(source=[Application.MOBILE, - Application.WEB]) \ No newline at end of file + Application.WEB]) diff --git a/apps/account/serializers/common.py b/apps/account/serializers/common.py index 92cf7a4b..7626dbb7 100644 --- a/apps/account/serializers/common.py +++ b/apps/account/serializers/common.py @@ -1,4 +1,4 @@ -"""Common serializers""" +"""Common account serializers""" from fcm_django.models import FCMDevice from rest_framework import serializers, exceptions diff --git a/apps/account/serializers/web.py b/apps/account/serializers/web.py index 17b097d7..6305c5a0 100644 --- a/apps/account/serializers/web.py +++ b/apps/account/serializers/web.py @@ -1,17 +1,47 @@ +"""Serializers for account web""" from django.contrib.auth import password_validation as password_validators from rest_framework import serializers +from account import models +from utils import exceptions as utils_exceptions -class PasswordResetConfirmSerializer(serializers.Serializer): - """Serializer for reset password""" - password = serializers.CharField(write_only=True) +class PasswordResetSerializer(serializers.ModelSerializer): + """Serializer from model PasswordReset""" - def validate_password(self, data): - """Custom password validation""" + class Meta: + """Meta class""" + model = models.ResetPasswordToken + fields = ('expiry_datetime', ) + + def create(self, validated_data, *args, **kwargs): + """Override create method""" + request = self.context.get('request') + user = request.user + ip_address = request.META.get('REMOTE_ADDR') + obj = models.ResetPasswordToken.objects.create( + user=user, + ip_address=ip_address, + source=models.ResetPasswordToken.MOBILE + ) try: - password_validators.validate_password(password=data) - except serializers.ValidationError as e: - raise serializers.ValidationError(str(e)) - else: - return data + # todo: make as celery task + obj.send_reset_password_request() + return obj + except: + raise utils_exceptions.EmailSendingError(user.email) + + +# class PasswordResetConfirmSerializer(serializers.Serializer): +# """Serializer for reset password""" +# +# password = serializers.CharField(write_only=True) +# +# def validate_password(self, data): +# """Custom password validation""" +# try: +# password_validators.validate_password(password=data) +# except serializers.ValidationError as e: +# raise serializers.ValidationError(str(e)) +# else: +# return data diff --git a/apps/account/urls/web.py b/apps/account/urls/web.py index d8733c0b..92f69e77 100644 --- a/apps/account/urls/web.py +++ b/apps/account/urls/web.py @@ -1,5 +1,4 @@ """Web account URLs""" -from django.contrib.auth.urls import urlpatterns as django_urls from django.urls import path from account.urls import common as common_views @@ -10,10 +9,9 @@ app_name = 'account' urlpatterns_api = [ path('reset-password/', views.PasswordResetView.as_view(), name='password-reset'), - path('reset-password//confirm', views.PasswordResetConfirmView.as_view(), - name='password-reset-confirm'), + # path('reset-password//confirm/', views.PasswordResetConfirmView.as_view(), + # name='password-reset-confirm'), ] urlpatterns = urlpatterns_api + \ - common_views.urlpatterns + \ - django_urls + common_views.urlpatterns diff --git a/apps/account/views/web.py b/apps/account/views/web.py index 199a73b7..f61923c8 100644 --- a/apps/account/views/web.py +++ b/apps/account/views/web.py @@ -1,36 +1,29 @@ """Web account views""" -from rest_framework import status, generics -from rest_framework.response import Response +from rest_framework import generics from account import models from account.serializers import web as serializers -from utils import exceptions as utils_exceptions # Password reset -class PasswordResetView(generics.GenericAPIView): +class PasswordResetView(generics.CreateAPIView): """View for resetting user password""" - def post(self, request, *args, **kwargs): - """Post-method for password resetting""" - user = request.user - obj = models.ResetPasswordToken.objects.create( - user=user, - ip_address=request.META.get('REMOTE_ADDR'), - source=models.ResetPasswordToken.MOBILE - ) - try: - # todo: make as celery task - obj.send_reset_password_request() - return Response(status=status.HTTP_200_OK) - except: - raise utils_exceptions.EmailSendingError(user.email) + serializer_class = serializers.PasswordResetSerializer + queryset = models.ResetPasswordToken -class PasswordResetConfirmView(generics.GenericAPIView): - """View for confirmation new password""" - - serializer_class = serializers.PasswordResetConfirmSerializer - - def post(self, request, *args, **kwargs): - """Post method to confirm user change password request""" +# class PasswordResetConfirmView(generics.GenericAPIView): +# """View for confirmation new password""" +# +# serializer_class = serializers.PasswordResetConfirmSerializer +# +# def post(self, request, *args, **kwargs): +# """Post method to confirm user change password request""" +# user = request.user +# token = kwargs.get('token') +# serializer = self.get_serializer(data=request.data) +# serializer.is_valid(raise_exception=True) +# if models.ResetPasswordToken.token_is_valid(user=user, +# token=token): +# pass diff --git a/project/settings/base.py b/project/settings/base.py index c73658e1..dd01b90d 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -78,6 +78,7 @@ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'oauth2_provider.middleware.OAuth2TokenMiddleware', + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', @@ -323,3 +324,7 @@ THUMBNAIL_ALIASES = { # Password reset RESETTING_TOKEN_EXPIRATION = 24 # hours + +# CORS Config +CORS_ORIGIN_ALLOW_ALL = True +CORS_ALLOW_CREDENTIALS = False diff --git a/requirements/base.txt b/requirements/base.txt index 9750559f..702708e5 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -21,4 +21,7 @@ amqp>=2.4.0 djangorestframework-oauth django-rest-framework-social-oauth2==1.1.0 -django-extensions==2.2.1 \ No newline at end of file +django-extensions==2.2.1 + +# CORS +django-cors-headers==3.0.2 \ No newline at end of file diff --git a/requirements/development.txt b/requirements/development.txt index e69de29b..79801f49 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -0,0 +1,2 @@ +-r base.txt +ipython \ No newline at end of file