added redirect for ConfirmEmail and ResetPassword
This commit is contained in:
parent
2622395006
commit
06c7d790bb
|
|
@ -94,9 +94,9 @@ class User(ImageMixin, AbstractUser):
|
||||||
self.is_active = switcher
|
self.is_active = switcher
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def create_jwt_tokens(self, source: int):
|
def create_jwt_tokens(self, source: int = None):
|
||||||
"""Create JWT tokens for user"""
|
"""Create JWT tokens for user"""
|
||||||
token = GMRefreshToken.for_user_by_source(self, source)
|
token = GMRefreshToken.for_user(self, source)
|
||||||
return {
|
return {
|
||||||
'access_token': str(token.access_token),
|
'access_token': str(token.access_token),
|
||||||
'refresh_token': str(token),
|
'refresh_token': str(token),
|
||||||
|
|
@ -154,15 +154,15 @@ class User(ImageMixin, AbstractUser):
|
||||||
"""Get base64 value for user by primary key identifier"""
|
"""Get base64 value for user by primary key identifier"""
|
||||||
return urlsafe_base64_encode(force_bytes(self.pk))
|
return urlsafe_base64_encode(force_bytes(self.pk))
|
||||||
|
|
||||||
@property
|
def confirm_email_template(self, country_code):
|
||||||
def confirm_email_template(self):
|
|
||||||
"""Get confirm email template"""
|
"""Get confirm email template"""
|
||||||
return render_to_string(
|
return render_to_string(
|
||||||
template_name=settings.CONFIRM_EMAIL_TEMPLATE,
|
template_name=settings.CONFIRM_EMAIL_TEMPLATE,
|
||||||
context={'token': self.confirm_email_token,
|
context={'token': self.confirm_email_token,
|
||||||
'uidb64': self.get_user_uidb64,
|
'uidb64': self.get_user_uidb64,
|
||||||
'domain_uri': settings.DOMAIN_URI,
|
'domain_uri': settings.DOMAIN_URI,
|
||||||
'site_name': settings.SITE_NAME})
|
'site_name': settings.SITE_NAME,
|
||||||
|
'country_code': country_code})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def change_email_template(self):
|
def change_email_template(self):
|
||||||
|
|
@ -245,15 +245,15 @@ class ResetPasswordToken(PlatformMixin, ProjectBaseMixin):
|
||||||
"""Generates a pseudo random code"""
|
"""Generates a pseudo random code"""
|
||||||
return password_token_generator.make_token(self.user)
|
return password_token_generator.make_token(self.user)
|
||||||
|
|
||||||
@property
|
def reset_password_template(self, country_code):
|
||||||
def reset_password_template(self):
|
|
||||||
"""Get reset password template"""
|
"""Get reset password template"""
|
||||||
return render_to_string(
|
return render_to_string(
|
||||||
template_name=settings.RESETTING_TOKEN_TEMPLATE,
|
template_name=settings.RESETTING_TOKEN_TEMPLATE,
|
||||||
context={'token': self.key,
|
context={'token': self.key,
|
||||||
'uidb64': self.user.get_user_uidb64,
|
'uidb64': self.user.get_user_uidb64,
|
||||||
'domain_uri': settings.DOMAIN_URI,
|
'domain_uri': settings.DOMAIN_URI,
|
||||||
'site_name': settings.SITE_NAME})
|
'site_name': settings.SITE_NAME,
|
||||||
|
'country_code': country_code})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def token_is_valid(user, token):
|
def token_is_valid(user, token):
|
||||||
|
|
|
||||||
|
|
@ -22,21 +22,27 @@ class PasswordResetSerializer(serializers.ModelSerializer):
|
||||||
'username_or_email',
|
'username_or_email',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def request(self):
|
||||||
|
"""Get request from context"""
|
||||||
|
return self.context.get('request')
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
"""Override validate method"""
|
"""Override validate method"""
|
||||||
user = self.context.get('request').user
|
user = self.request.user
|
||||||
|
|
||||||
if user.is_anonymous:
|
if user.is_anonymous:
|
||||||
username_or_email = attrs.get('username_or_email')
|
username_or_email = attrs.get('username_or_email')
|
||||||
if not username_or_email:
|
if not username_or_email:
|
||||||
raise serializers.ValidationError(_('Username or Email not in request body.'))
|
raise serializers.ValidationError(_('Username or Email not in request body.'))
|
||||||
# Check user in DB
|
# Check user in DB
|
||||||
username_or_email = (username_or_email.lower()
|
filters = {}
|
||||||
if username_validator(username_or_email) is False
|
if username_validator(username_or_email):
|
||||||
else username_or_email)
|
filters.update({'username': username_or_email})
|
||||||
user_qs = models.User.objects.filter(Q(email=username_or_email) |
|
else:
|
||||||
Q(username=username_or_email))
|
filters.update({'email': username_or_email.lower()})
|
||||||
if user_qs.exists():
|
user_qs = models.User.objects.filter(**filters)
|
||||||
|
if user_qs.exists() and filters:
|
||||||
attrs['user'] = user_qs.first()
|
attrs['user'] = user_qs.first()
|
||||||
else:
|
else:
|
||||||
attrs['user'] = user
|
attrs['user'] = user
|
||||||
|
|
@ -45,16 +51,18 @@ class PasswordResetSerializer(serializers.ModelSerializer):
|
||||||
def create(self, validated_data, *args, **kwargs):
|
def create(self, validated_data, *args, **kwargs):
|
||||||
"""Override create method"""
|
"""Override create method"""
|
||||||
user = validated_data.pop('user')
|
user = validated_data.pop('user')
|
||||||
ip_address = self.context.get('request').META.get('REMOTE_ADDR')
|
ip_address = self.request.META.get('REMOTE_ADDR')
|
||||||
|
|
||||||
obj = models.ResetPasswordToken.objects.create(
|
obj = models.ResetPasswordToken.objects.create(
|
||||||
user=user,
|
user=user,
|
||||||
ip_address=ip_address,
|
ip_address=ip_address,
|
||||||
source=models.ResetPasswordToken.WEB)
|
source=models.ResetPasswordToken.WEB)
|
||||||
if settings.USE_CELERY:
|
if settings.USE_CELERY:
|
||||||
tasks.send_reset_password_email.delay(obj.id)
|
tasks.send_reset_password_email.delay(request_id=obj.id,
|
||||||
|
country_code=self.request.country_code)
|
||||||
else:
|
else:
|
||||||
tasks.send_reset_password_email(obj.id)
|
tasks.send_reset_password_email(request_id=obj.id,
|
||||||
|
country_code=self.request.country_code)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,13 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def send_reset_password_email(request_id):
|
def send_reset_password_email(request_id, country_code):
|
||||||
"""Send email to user for reset password."""
|
"""Send email to user for reset password."""
|
||||||
try:
|
try:
|
||||||
obj = models.ResetPasswordToken.objects.get(id=request_id)
|
obj = models.ResetPasswordToken.objects.get(id=request_id)
|
||||||
user = obj.user
|
user = obj.user
|
||||||
user.send_email(subject=_('Password resetting'),
|
user.send_email(subject=_('Password resetting'),
|
||||||
message=obj.reset_password_template)
|
message=obj.reset_password_template(country_code))
|
||||||
except:
|
except:
|
||||||
logger.error(f'METHOD_NAME: {send_reset_password_email.__name__}\n'
|
logger.error(f'METHOD_NAME: {send_reset_password_email.__name__}\n'
|
||||||
f'DETAIL: Exception occurred for ResetPasswordToken instance: '
|
f'DETAIL: Exception occurred for ResetPasswordToken instance: '
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ from rest_framework import generics
|
||||||
from rest_framework import permissions
|
from rest_framework import permissions
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from account import models
|
from account import models
|
||||||
from account.serializers import common as serializers
|
from account.serializers import common as serializers
|
||||||
|
|
@ -29,6 +30,31 @@ class ChangePasswordView(JWTUpdateAPIView):
|
||||||
"""Change password view"""
|
"""Change password view"""
|
||||||
serializer_class = serializers.ChangePasswordSerializer
|
serializer_class = serializers.ChangePasswordSerializer
|
||||||
queryset = models.User.objects.active()
|
queryset = models.User.objects.active()
|
||||||
|
permission_classes = (permissions.AllowAny, )
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
"""Overridden get_object method."""
|
||||||
|
if not self.request.user.is_authenticated():
|
||||||
|
queryset = self.filter_queryset(self.get_queryset())
|
||||||
|
uidb64 = self.kwargs.get('uidb64')
|
||||||
|
|
||||||
|
user_id = force_text(urlsafe_base64_decode(uidb64))
|
||||||
|
token = self.kwargs.get('token')
|
||||||
|
|
||||||
|
filter_kwargs = {'key': token, 'user_id': user_id}
|
||||||
|
password_reset_obj = get_object_or_404(models.ResetPasswordToken.objects.valid(),
|
||||||
|
**filter_kwargs)
|
||||||
|
if not GMTokenGenerator(GMTokenGenerator.RESET_PASSWORD).check_token(
|
||||||
|
user=password_reset_obj.user, token=token):
|
||||||
|
raise utils_exceptions.NotValidAccessTokenError()
|
||||||
|
# todo: Add is_valid check status
|
||||||
|
obj = password_reset_obj.user
|
||||||
|
else:
|
||||||
|
obj = self.request.user
|
||||||
|
|
||||||
|
# May raise a permission denied
|
||||||
|
self.check_object_permissions(self.request, obj)
|
||||||
|
return obj
|
||||||
|
|
||||||
def patch(self, request, *args, **kwargs):
|
def patch(self, request, *args, **kwargs):
|
||||||
"""Implement PUT method"""
|
"""Implement PUT method"""
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,8 @@ class Application(PlatformMixin, AbstractApplication):
|
||||||
|
|
||||||
class JWTAccessTokenManager(models.Manager):
|
class JWTAccessTokenManager(models.Manager):
|
||||||
"""Manager for AccessToken model."""
|
"""Manager for AccessToken model."""
|
||||||
def add_to_db(self, user, access_token: AccessToken,
|
|
||||||
refresh_token: RefreshToken):
|
def make(self, user, access_token: AccessToken, refresh_token: RefreshToken):
|
||||||
"""Create generated tokens to DB"""
|
"""Create generated tokens to DB"""
|
||||||
refresh_token_qs = JWTRefreshToken.objects.filter(user=user,
|
refresh_token_qs = JWTRefreshToken.objects.filter(user=user,
|
||||||
jti=refresh_token.payload.get('jti'))
|
jti=refresh_token.payload.get('jti'))
|
||||||
|
|
@ -106,18 +106,17 @@ class JWTAccessToken(ProjectBaseMixin):
|
||||||
|
|
||||||
class JWTRefreshTokenManager(models.Manager):
|
class JWTRefreshTokenManager(models.Manager):
|
||||||
"""Manager for model RefreshToken."""
|
"""Manager for model RefreshToken."""
|
||||||
|
def make(self, user, token: RefreshToken, source: int):
|
||||||
def add_to_db(self, user, token: RefreshToken, source: int):
|
"""Make method"""
|
||||||
"""Added generated refresh token to db"""
|
|
||||||
jti = token[settings.SIMPLE_JWT.get('JTI_CLAIM')]
|
jti = token[settings.SIMPLE_JWT.get('JTI_CLAIM')]
|
||||||
exp = token['exp']
|
exp = token['exp']
|
||||||
obj = self.model(
|
obj = self.model(
|
||||||
user=user,
|
user=user,
|
||||||
jti=jti,
|
jti=jti,
|
||||||
source=source,
|
|
||||||
created_at=token.current_time,
|
created_at=token.current_time,
|
||||||
expires_at=utils.datetime_from_epoch(exp),
|
expires_at=utils.datetime_from_epoch(exp))
|
||||||
)
|
if source:
|
||||||
|
obj.source = source
|
||||||
obj.save()
|
obj.save()
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,9 @@ from utils.tokens import GMRefreshToken
|
||||||
class SignupSerializer(serializers.ModelSerializer):
|
class SignupSerializer(serializers.ModelSerializer):
|
||||||
"""Signup serializer serializer mixin"""
|
"""Signup serializer serializer mixin"""
|
||||||
# REQUEST
|
# REQUEST
|
||||||
username = serializers.CharField(
|
username = serializers.CharField(write_only=True)
|
||||||
validators=(rest_validators.UniqueValidator(queryset=account_models.User.objects.all()),),
|
|
||||||
write_only=True)
|
|
||||||
password = serializers.CharField(write_only=True)
|
password = serializers.CharField(write_only=True)
|
||||||
email = serializers.EmailField(
|
email = serializers.EmailField(write_only=True)
|
||||||
validators=(rest_validators.UniqueValidator(queryset=account_models.User.objects.all()),),
|
|
||||||
write_only=True)
|
|
||||||
newsletter = serializers.BooleanField(write_only=True)
|
newsletter = serializers.BooleanField(write_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -42,6 +38,14 @@ class SignupSerializer(serializers.ModelSerializer):
|
||||||
valid = utils_methods.username_validator(username=value)
|
valid = utils_methods.username_validator(username=value)
|
||||||
if not valid:
|
if not valid:
|
||||||
raise utils_exceptions.NotValidUsernameError()
|
raise utils_exceptions.NotValidUsernameError()
|
||||||
|
if account_models.User.objects.filter(username__icontains=value).exists():
|
||||||
|
raise serializers.ValidationError()
|
||||||
|
return value
|
||||||
|
|
||||||
|
def validate_email(self, value):
|
||||||
|
"""Validate email"""
|
||||||
|
if account_models.User.objects.filter(email__icontains=value).exists():
|
||||||
|
raise serializers.ValidationError()
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def validate_password(self, value):
|
def validate_password(self, value):
|
||||||
|
|
@ -62,9 +66,13 @@ class SignupSerializer(serializers.ModelSerializer):
|
||||||
newsletter=validated_data.get('newsletter'))
|
newsletter=validated_data.get('newsletter'))
|
||||||
# Send verification link on user email
|
# Send verification link on user email
|
||||||
if settings.USE_CELERY:
|
if settings.USE_CELERY:
|
||||||
tasks.send_confirm_email.delay(obj.id)
|
tasks.send_confirm_email.delay(
|
||||||
|
user_id=obj.id,
|
||||||
|
country_code=self.context.get('request').country_code)
|
||||||
else:
|
else:
|
||||||
tasks.send_confirm_email(obj.id)
|
tasks.send_confirm_email(
|
||||||
|
user_id=obj.id,
|
||||||
|
country_code=self.context.get('request').country_code)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -128,14 +136,10 @@ class RefreshTokenSerializer(SourceSerializerMixin):
|
||||||
refresh_token = serializers.CharField(read_only=True)
|
refresh_token = serializers.CharField(read_only=True)
|
||||||
access_token = serializers.CharField(read_only=True)
|
access_token = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
def get_request(self):
|
|
||||||
"""Return request"""
|
|
||||||
return self.context.get('request')
|
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
"""Override validate method"""
|
"""Override validate method"""
|
||||||
|
|
||||||
cookie_refresh_token = self.get_request().COOKIES.get('refresh_token')
|
cookie_refresh_token = self.context.get('request').COOKIES.get('refresh_token')
|
||||||
# Check if refresh_token in COOKIES
|
# Check if refresh_token in COOKIES
|
||||||
if not cookie_refresh_token:
|
if not cookie_refresh_token:
|
||||||
raise utils_exceptions.NotValidRefreshTokenError()
|
raise utils_exceptions.NotValidRefreshTokenError()
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def send_confirm_email(user_id):
|
def send_confirm_email(user_id, country_code):
|
||||||
"""Send verification email to user."""
|
"""Send verification email to user."""
|
||||||
try:
|
try:
|
||||||
obj = account_models.User.objects.get(id=user_id)
|
obj = account_models.User.objects.get(id=user_id)
|
||||||
obj.send_email(subject=_('Email confirmation'),
|
obj.send_email(subject=_('Email confirmation'),
|
||||||
message=obj.confirm_email_template)
|
message=obj.confirm_email_template(country_code))
|
||||||
except:
|
except:
|
||||||
logger.error(f'METHOD_NAME: {send_confirm_email.__name__}\n'
|
logger.error(f'METHOD_NAME: {send_confirm_email.__name__}\n'
|
||||||
f'DETAIL: Exception occurred for user: {user_id}')
|
f'DETAIL: Exception occurred for user: {user_id}')
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,15 @@ class VerifyEmailConfirmView(JWTGenericViewMixin):
|
||||||
raise utils_exceptions.NotValidTokenError()
|
raise utils_exceptions.NotValidTokenError()
|
||||||
# Approve email status
|
# Approve email status
|
||||||
user.confirm_email()
|
user.confirm_email()
|
||||||
return Response(status=status.HTTP_200_OK)
|
response = Response(status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
# Create tokens
|
||||||
|
tokens = user.create_jwt_tokens()
|
||||||
|
return self._put_cookies_in_response(
|
||||||
|
cookies=self._put_data_in_cookies(
|
||||||
|
access_token=tokens.get('access_token'),
|
||||||
|
refresh_token=tokens.get('refresh_token')),
|
||||||
|
response=response)
|
||||||
else:
|
else:
|
||||||
raise utils_exceptions.UserNotFoundError()
|
raise utils_exceptions.UserNotFoundError()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,11 @@ class GMBlacklistMixin(BlacklistMixin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def for_user_by_source(cls, user, source: int):
|
def for_user(cls, user, source: int = None):
|
||||||
"""Create a refresh token."""
|
"""Create a refresh token."""
|
||||||
token = super().for_user(user)
|
token = super().for_user(user)
|
||||||
token['user'] = user.get_user_info()
|
token['user'] = user.get_user_info()
|
||||||
# Create a record in DB
|
JWTRefreshToken.objects.make(user=user, token=token, source=source)
|
||||||
JWTRefreshToken.objects.add_to_db(user=user, token=token, source=source)
|
|
||||||
return token
|
return token
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -70,7 +69,7 @@ class GMRefreshToken(GMBlacklistMixin, GMToken, RefreshToken):
|
||||||
|
|
||||||
# Create a record in DB
|
# Create a record in DB
|
||||||
user = User.objects.get(id=self.payload.get('user_id'))
|
user = User.objects.get(id=self.payload.get('user_id'))
|
||||||
JWTAccessToken.objects.add_to_db(user=user,
|
JWTAccessToken.objects.make(user=user,
|
||||||
access_token=access_token,
|
access_token=access_token,
|
||||||
refresh_token=self)
|
refresh_token=self)
|
||||||
return access_token
|
return access_token
|
||||||
|
|
|
||||||
17
project/settings/stage.py
Normal file
17
project/settings/stage.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
"""Stage settings."""
|
||||||
|
from .base import *
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = ['gm-stage.id-east.ru', '95.213.204.126']
|
||||||
|
|
||||||
|
SEND_SMS = False
|
||||||
|
SMS_CODE_SHOW = True
|
||||||
|
USE_CELERY = False
|
||||||
|
|
||||||
|
SCHEMA_URI = 'https'
|
||||||
|
DEFAULT_SUBDOMAIN = 'www'
|
||||||
|
SITE_DOMAIN_URI = 'id-east.ru'
|
||||||
|
DOMAIN_URI = 'gm-stage.id-east.ru'
|
||||||
|
|
||||||
|
# COOKIES
|
||||||
|
CSRF_COOKIE_DOMAIN = '.id-east.ru'
|
||||||
|
SESSION_COOKIE_DOMAIN = '.id-east.ru'
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
{% trans "Please go to the following page and choose a new password:" %}
|
{% trans "Please go to the following page and choose a new password:" %}
|
||||||
{% block reset_link %}
|
{% block reset_link %}
|
||||||
http://{{ domain_uri }}{% url 'web:account:form-password-reset-confirm' uidb64=uidb64 token=token %}
|
<a href="https://{{ country_code }}.{{ domain_uri }}/recovery/{{ uidb64 }}/{{ token }}/">Reset link.</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% trans "Thanks for using our site!" %}
|
{% trans "Thanks for using our site!" %}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,8 @@
|
||||||
{% blocktrans %}You're receiving this email because you trying to register new account at {{ site_name }}.{% endblocktrans %}
|
{% blocktrans %}You're receiving this email because you trying to register new account at {{ site_name }}.{% endblocktrans %}
|
||||||
|
|
||||||
{% trans "Please confirm your email address to complete the registration:" %}
|
{% trans "Please confirm your email address to complete the registration:" %}
|
||||||
{% block signup_confirm %}
|
|
||||||
http://{{ domain_uri }}{% url 'auth:signup-confirm' uidb64=uidb64 token=token %}
|
<a href="https://{{ country_code }}.{{ domain_uri }}/registered/{{ uidb64 }}/{{ token }}/">Confirmation link.</a>
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% trans "Thanks for using our site!" %}
|
{% trans "Thanks for using our site!" %}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user