diff --git a/apps/account/models.py b/apps/account/models.py
index 508a52fd..30255802 100644
--- a/apps/account/models.py
+++ b/apps/account/models.py
@@ -94,9 +94,9 @@ class User(ImageMixin, AbstractUser):
self.is_active = switcher
self.save()
- def create_jwt_tokens(self, source: int):
+ def create_jwt_tokens(self, source: int = None):
"""Create JWT tokens for user"""
- token = GMRefreshToken.for_user_by_source(self, source)
+ token = GMRefreshToken.for_user(self, source)
return {
'access_token': str(token.access_token),
'refresh_token': str(token),
@@ -154,15 +154,15 @@ class User(ImageMixin, AbstractUser):
"""Get base64 value for user by primary key identifier"""
return urlsafe_base64_encode(force_bytes(self.pk))
- @property
- def confirm_email_template(self):
+ def confirm_email_template(self, country_code):
"""Get confirm email template"""
return render_to_string(
template_name=settings.CONFIRM_EMAIL_TEMPLATE,
context={'token': self.confirm_email_token,
'uidb64': self.get_user_uidb64,
'domain_uri': settings.DOMAIN_URI,
- 'site_name': settings.SITE_NAME})
+ 'site_name': settings.SITE_NAME,
+ 'country_code': country_code})
@property
def change_email_template(self):
@@ -245,15 +245,15 @@ class ResetPasswordToken(PlatformMixin, ProjectBaseMixin):
"""Generates a pseudo random code"""
return password_token_generator.make_token(self.user)
- @property
- def reset_password_template(self):
+ def reset_password_template(self, country_code):
"""Get reset password template"""
return render_to_string(
template_name=settings.RESETTING_TOKEN_TEMPLATE,
context={'token': self.key,
'uidb64': self.user.get_user_uidb64,
'domain_uri': settings.DOMAIN_URI,
- 'site_name': settings.SITE_NAME})
+ 'site_name': settings.SITE_NAME,
+ 'country_code': country_code})
@staticmethod
def token_is_valid(user, token):
diff --git a/apps/account/serializers/web.py b/apps/account/serializers/web.py
index 60a68820..cdf616dc 100644
--- a/apps/account/serializers/web.py
+++ b/apps/account/serializers/web.py
@@ -22,21 +22,27 @@ class PasswordResetSerializer(serializers.ModelSerializer):
'username_or_email',
)
+ @property
+ def request(self):
+ """Get request from context"""
+ return self.context.get('request')
+
def validate(self, attrs):
"""Override validate method"""
- user = self.context.get('request').user
+ user = self.request.user
if user.is_anonymous:
username_or_email = attrs.get('username_or_email')
if not username_or_email:
raise serializers.ValidationError(_('Username or Email not in request body.'))
# Check user in DB
- username_or_email = (username_or_email.lower()
- if username_validator(username_or_email) is False
- else username_or_email)
- user_qs = models.User.objects.filter(Q(email=username_or_email) |
- Q(username=username_or_email))
- if user_qs.exists():
+ filters = {}
+ if username_validator(username_or_email):
+ filters.update({'username': username_or_email})
+ else:
+ filters.update({'email': username_or_email.lower()})
+ user_qs = models.User.objects.filter(**filters)
+ if user_qs.exists() and filters:
attrs['user'] = user_qs.first()
else:
attrs['user'] = user
@@ -45,16 +51,18 @@ class PasswordResetSerializer(serializers.ModelSerializer):
def create(self, validated_data, *args, **kwargs):
"""Override create method"""
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(
user=user,
ip_address=ip_address,
source=models.ResetPasswordToken.WEB)
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:
- tasks.send_reset_password_email(obj.id)
+ tasks.send_reset_password_email(request_id=obj.id,
+ country_code=self.request.country_code)
return obj
diff --git a/apps/account/tasks.py b/apps/account/tasks.py
index 0367a59e..362daddf 100644
--- a/apps/account/tasks.py
+++ b/apps/account/tasks.py
@@ -11,13 +11,13 @@ logger = logging.getLogger(__name__)
@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."""
try:
obj = models.ResetPasswordToken.objects.get(id=request_id)
user = obj.user
user.send_email(subject=_('Password resetting'),
- message=obj.reset_password_template)
+ message=obj.reset_password_template(country_code))
except:
logger.error(f'METHOD_NAME: {send_reset_password_email.__name__}\n'
f'DETAIL: Exception occurred for ResetPasswordToken instance: '
diff --git a/apps/account/views/common.py b/apps/account/views/common.py
index 2e182bd9..360a1e5a 100644
--- a/apps/account/views/common.py
+++ b/apps/account/views/common.py
@@ -6,6 +6,7 @@ from rest_framework import generics
from rest_framework import permissions
from rest_framework import status
from rest_framework.response import Response
+from django.shortcuts import get_object_or_404
from account import models
from account.serializers import common as serializers
@@ -29,6 +30,31 @@ class ChangePasswordView(JWTUpdateAPIView):
"""Change password view"""
serializer_class = serializers.ChangePasswordSerializer
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):
"""Implement PUT method"""
diff --git a/apps/authorization/models.py b/apps/authorization/models.py
index 69416420..c295329c 100644
--- a/apps/authorization/models.py
+++ b/apps/authorization/models.py
@@ -38,8 +38,8 @@ class Application(PlatformMixin, AbstractApplication):
class JWTAccessTokenManager(models.Manager):
"""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"""
refresh_token_qs = JWTRefreshToken.objects.filter(user=user,
jti=refresh_token.payload.get('jti'))
@@ -106,18 +106,17 @@ class JWTAccessToken(ProjectBaseMixin):
class JWTRefreshTokenManager(models.Manager):
"""Manager for model RefreshToken."""
-
- def add_to_db(self, user, token: RefreshToken, source: int):
- """Added generated refresh token to db"""
+ def make(self, user, token: RefreshToken, source: int):
+ """Make method"""
jti = token[settings.SIMPLE_JWT.get('JTI_CLAIM')]
exp = token['exp']
obj = self.model(
user=user,
jti=jti,
- source=source,
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()
return obj
diff --git a/apps/authorization/serializers/common.py b/apps/authorization/serializers/common.py
index f5e24fc9..4d29887c 100644
--- a/apps/authorization/serializers/common.py
+++ b/apps/authorization/serializers/common.py
@@ -19,13 +19,9 @@ from utils.tokens import GMRefreshToken
class SignupSerializer(serializers.ModelSerializer):
"""Signup serializer serializer mixin"""
# REQUEST
- username = serializers.CharField(
- validators=(rest_validators.UniqueValidator(queryset=account_models.User.objects.all()),),
- write_only=True)
+ username = serializers.CharField(write_only=True)
password = serializers.CharField(write_only=True)
- email = serializers.EmailField(
- validators=(rest_validators.UniqueValidator(queryset=account_models.User.objects.all()),),
- write_only=True)
+ email = serializers.EmailField(write_only=True)
newsletter = serializers.BooleanField(write_only=True)
class Meta:
@@ -42,6 +38,14 @@ class SignupSerializer(serializers.ModelSerializer):
valid = utils_methods.username_validator(username=value)
if not valid:
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
def validate_password(self, value):
@@ -62,9 +66,13 @@ class SignupSerializer(serializers.ModelSerializer):
newsletter=validated_data.get('newsletter'))
# Send verification link on user email
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:
- tasks.send_confirm_email(obj.id)
+ tasks.send_confirm_email(
+ user_id=obj.id,
+ country_code=self.context.get('request').country_code)
return obj
@@ -128,14 +136,10 @@ class RefreshTokenSerializer(SourceSerializerMixin):
refresh_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):
"""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
if not cookie_refresh_token:
raise utils_exceptions.NotValidRefreshTokenError()
diff --git a/apps/authorization/tasks.py b/apps/authorization/tasks.py
index a2ae4bb3..9947c2a3 100644
--- a/apps/authorization/tasks.py
+++ b/apps/authorization/tasks.py
@@ -10,12 +10,12 @@ logger = logging.getLogger(__name__)
@shared_task
-def send_confirm_email(user_id):
+def send_confirm_email(user_id, country_code):
"""Send verification email to user."""
try:
obj = account_models.User.objects.get(id=user_id)
obj.send_email(subject=_('Email confirmation'),
- message=obj.confirm_email_template)
+ message=obj.confirm_email_template(country_code))
except:
logger.error(f'METHOD_NAME: {send_confirm_email.__name__}\n'
f'DETAIL: Exception occurred for user: {user_id}')
diff --git a/apps/authorization/views/common.py b/apps/authorization/views/common.py
index c77dd63e..827ff108 100644
--- a/apps/authorization/views/common.py
+++ b/apps/authorization/views/common.py
@@ -182,7 +182,15 @@ class VerifyEmailConfirmView(JWTGenericViewMixin):
raise utils_exceptions.NotValidTokenError()
# Approve email status
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:
raise utils_exceptions.UserNotFoundError()
diff --git a/apps/utils/tokens.py b/apps/utils/tokens.py
index e686b42b..a236bfa3 100644
--- a/apps/utils/tokens.py
+++ b/apps/utils/tokens.py
@@ -33,12 +33,11 @@ class GMBlacklistMixin(BlacklistMixin):
"""
@classmethod
- def for_user_by_source(cls, user, source: int):
+ def for_user(cls, user, source: int = None):
"""Create a refresh token."""
token = super().for_user(user)
token['user'] = user.get_user_info()
- # Create a record in DB
- JWTRefreshToken.objects.add_to_db(user=user, token=token, source=source)
+ JWTRefreshToken.objects.make(user=user, token=token, source=source)
return token
@@ -70,7 +69,7 @@ class GMRefreshToken(GMBlacklistMixin, GMToken, RefreshToken):
# Create a record in DB
user = User.objects.get(id=self.payload.get('user_id'))
- JWTAccessToken.objects.add_to_db(user=user,
- access_token=access_token,
- refresh_token=self)
+ JWTAccessToken.objects.make(user=user,
+ access_token=access_token,
+ refresh_token=self)
return access_token
diff --git a/project/settings/stage.py b/project/settings/stage.py
new file mode 100644
index 00000000..998abaa6
--- /dev/null
+++ b/project/settings/stage.py
@@ -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'
diff --git a/project/templates/account/password_reset_email.html b/project/templates/account/password_reset_email.html
index 1a395cee..b743d71c 100644
--- a/project/templates/account/password_reset_email.html
+++ b/project/templates/account/password_reset_email.html
@@ -3,7 +3,7 @@
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
-http://{{ domain_uri }}{% url 'web:account:form-password-reset-confirm' uidb64=uidb64 token=token %}
+Reset link.
{% endblock %}
{% trans "Thanks for using our site!" %}
diff --git a/project/templates/authorization/confirm_email.html b/project/templates/authorization/confirm_email.html
index 7fa06aa5..9056525d 100644
--- a/project/templates/authorization/confirm_email.html
+++ b/project/templates/authorization/confirm_email.html
@@ -2,9 +2,8 @@
{% 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:" %}
-{% block signup_confirm %}
-http://{{ domain_uri }}{% url 'auth:signup-confirm' uidb64=uidb64 token=token %}
-{% endblock %}
+
+Confirmation link.
{% trans "Thanks for using our site!" %}