Merge branch 'backend' into 'master'

add user reset password

See merge request dm1sh/hackathon2020_komap!3
This commit is contained in:
Dmitriy Shishkov 2020-11-28 18:11:42 +05:00
commit 9c97eb64b4
6 changed files with 98 additions and 3 deletions

View File

@ -147,3 +147,21 @@ EMAIL_HOST_USER = 'artemss20030906@gmail.com'
EMAIL_HOST_PASSWORD = 'hbqgvdsqhvkuijuc' EMAIL_HOST_PASSWORD = 'hbqgvdsqhvkuijuc'
EMAIL_PORT = 587 EMAIL_PORT = 587
EMAIL_USE_TLS = True EMAIL_USE_TLS = True
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 7,
}
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]

View File

@ -7,6 +7,9 @@ class Profile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE) user = models.OneToOneField(User, on_delete = models.CASCADE)
points = models.DecimalField(max_digits=7, decimal_places=0, default = 0) points = models.DecimalField(max_digits=7, decimal_places=0, default = 0)
def __str__(self):
return(f'{self.user.username} - {self.user.email}')
@receiver(post_save, sender = User) @receiver(post_save, sender = User)
def create_profile(sender, instance, created, **kwargs): def create_profile(sender, instance, created, **kwargs):
if(created): if(created):

View File

@ -10,4 +10,19 @@ class UserRegisterSerializer(serializers.ModelSerializer):
'username', 'username',
'password', 'password',
'email' 'email'
]
class UserResetPasswordSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=True)
class Meta:
model = User
fields = [
'email'
]
class UserPasswordSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'password'
] ]

View File

@ -7,4 +7,6 @@ urlpatterns = [
path('login', obtain_auth_token, name = 'UserLogin'), path('login', obtain_auth_token, name = 'UserLogin'),
path('logout', views.UserLogoutAPIView, name = 'UserLogout'), path('logout', views.UserLogoutAPIView, name = 'UserLogout'),
path('activate/<uidb64>/<token>', views.UserActivationAPIView, name = 'UserActivation'), path('activate/<uidb64>/<token>', views.UserActivationAPIView, name = 'UserActivation'),
path('reset_password_request', views.UserResetPasswordRequestAPIView, name = 'UserResetPasswordRequest'),
path('reset_password/<uidb64>/<token>', views.UserResetPasswordAPIView, name = 'UserResetPassword'),
] ]

View File

@ -10,8 +10,9 @@ from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.core.mail import EmailMultiAlternatives from django.core.mail import EmailMultiAlternatives
from .serializers import UserRegisterSerializer from .serializers import UserRegisterSerializer, UserResetPasswordSerializer, UserPasswordSerializer
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.contrib.auth.password_validation import validate_password
import six import six
import threading import threading
@ -20,6 +21,7 @@ class EmailTokenGenerator(PasswordResetTokenGenerator):
return(six.text_type(user.pk) + six.text_type(timestamp) + six.text_type(user.is_active)) return(six.text_type(user.pk) + six.text_type(timestamp) + six.text_type(user.is_active))
email_token_gen = EmailTokenGenerator() email_token_gen = EmailTokenGenerator()
password_token_gen = PasswordResetTokenGenerator()
@api_view(['POST']) @api_view(['POST'])
@permission_classes([~permissions.IsAuthenticated]) @permission_classes([~permissions.IsAuthenticated])
@ -27,8 +29,12 @@ def UserRegisterAPIView(request):
serializer = UserRegisterSerializer(data = request.data) serializer = UserRegisterSerializer(data = request.data)
if(serializer.is_valid()): if(serializer.is_valid()):
data = serializer.validated_data data = serializer.validated_data
if(User.objects.filter(username = data['username']).exists()): if(User.objects.filter(username = data['username']).exists() or User.objects.filter(email = data['email']).exists()):
return Response({'ok':False, 'error':'User already exists'}, status = status.HTTP_400_BAD_REQUEST) return Response({'ok':False, 'error':'User already exists'}, status = status.HTTP_400_BAD_REQUEST)
try:
validate_password(data['password'])
except Exception as errors:
return Response({'ok':False, 'error':errors} ,status = status.HTTP_400_BAD_REQUEST)
user = User.objects.create(username = data['username'], email = data['email'], is_active = False) user = User.objects.create(username = data['username'], email = data['email'], is_active = False)
user.set_password(data['password']) user.set_password(data['password'])
user.save() user.save()
@ -52,12 +58,57 @@ def UserActivationAPIView(request, uidb64, token):
user = User.objects.filter(id = uid) user = User.objects.filter(id = uid)
if(user.exists()): if(user.exists()):
user = user.first() user = user.first()
if(email_token_gen.check_token(user, token) and not user.is_active): if(email_token_gen.check_token(user, token)):
user.is_active = True user.is_active = True
user.save() user.save()
return Response({'ok':True}, status = status.HTTP_200_OK) return Response({'ok':True}, status = status.HTTP_200_OK)
return Response({'ok':False, 'error':'Invalid activation link'}, status = status.HTTP_400_BAD_REQUEST) return Response({'ok':False, 'error':'Invalid activation link'}, status = status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@permission_classes([~permissions.IsAuthenticated])
def UserResetPasswordRequestAPIView(request):
serializer = UserResetPasswordSerializer(data = request.data)
if(serializer.is_valid()):
data = serializer.validated_data
user = User.objects.filter(email = data['email'])
if(user.exists()):
user = user.first()
domain = get_current_site(request).domain
mail_subject = 'Password reset'
message = render_to_string('password_reset.html',{
'user':user,
'domain': domain,
'uidb64': urlsafe_base64_encode(force_bytes(user.pk)),
'token': password_token_gen.make_token(user),
})
message_task = threading.Thread(target = async_email_send, args=( mail_subject, message, [data['email']] ))
message_task.start()
return Response({'ok':True}, status = status.HTTP_200_OK)
return Response({'ok':False, 'error':'User does not exist'}, status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':serializer.errors}, status = status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@permission_classes([~permissions.IsAuthenticated])
def UserResetPasswordAPIView(request, uidb64, token):
serializer = UserPasswordSerializer(data = request.data)
if(serializer.is_valid()):
data = serializer.validated_data
try:
validate_password(data['password'])
except Exception as errors:
return Response({'ok':False, 'error':errors} ,status = status.HTTP_400_BAD_REQUEST)
uid = force_text(urlsafe_base64_decode(uidb64))
user = User.objects.filter(id = uid)
if(user.exists()):
user = user.first()
if(password_token_gen.check_token(user, token)):
user.set_password(data['password'])
user.save()
return Response({'ok':True}, status = status.HTTP_200_OK)
return Response({'ok':False, 'error':'Invalid password reset link'} ,status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':'User does not exist'} ,status = status.HTTP_400_BAD_REQUEST)
return Response({'ok':False, 'error':serializer.errors}, status = status.HTTP_400_BAD_REQUEST)
@api_view(['POST']) @api_view(['POST'])
@permission_classes([permissions.IsAuthenticated]) @permission_classes([permissions.IsAuthenticated])
def UserLogoutAPIView(request): def UserLogoutAPIView(request):

View File

@ -0,0 +1,6 @@
{% autoescape off %}
<h1>HI {{ user.username }}</h1>
<h2>This email was used to reset your account password</h2>
<h2>To reset password follow the link below:</h2>
<a href="http://{{ domain }}/reset_pass/{{ uidb64 }}/{{ token }}"><h2>Reset Password</h2></a>
{% endautoescape %}