refactored apps account, authorization, news, utils
This commit is contained in:
parent
ec2e87fe1a
commit
075298f6ec
|
|
@ -46,12 +46,3 @@ class UserAdmin(BaseUserAdmin):
|
||||||
return obj.get_short_name()
|
return obj.get_short_name()
|
||||||
|
|
||||||
short_name.short_description = _('Name')
|
short_name.short_description = _('Name')
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.ResetPasswordToken)
|
|
||||||
class ResetPasswordToken(admin.ModelAdmin):
|
|
||||||
"""Model admin for ResetPasswordToken"""
|
|
||||||
list_display = ('id', 'user', 'expiry_datetime')
|
|
||||||
list_filter = ('expiry_datetime', 'user')
|
|
||||||
search_fields = ('user', )
|
|
||||||
readonly_fields = ('user', 'key', )
|
|
||||||
|
|
|
||||||
16
apps/account/migrations/0006_delete_resetpasswordtoken.py
Normal file
16
apps/account/migrations/0006_delete_resetpasswordtoken.py
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-09-12 11:42
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('account', '0005_user_cropped_image'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='ResetPasswordToken',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -147,120 +147,43 @@ class User(ImageMixin, AbstractUser):
|
||||||
@property
|
@property
|
||||||
def reset_password_token(self):
|
def reset_password_token(self):
|
||||||
"""Make a token for finish signup."""
|
"""Make a token for finish signup."""
|
||||||
return GMTokenGenerator(purpose=GMTokenGenerator.RESET_PASSWORD).make_token(self)
|
return password_token_generator.make_token(self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def get_user_uidb64(self):
|
def get_user_uidb64(self):
|
||||||
"""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))
|
||||||
|
|
||||||
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,
|
|
||||||
'country_code': country_code})
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def change_email_template(self):
|
def base_template(self):
|
||||||
"""Get change email template"""
|
"""Base email template"""
|
||||||
return render_to_string(
|
return {'domain_uri': settings.DOMAIN_URI,
|
||||||
template_name=settings.CHANGE_EMAIL_TEMPLATE,
|
'uidb64': self.get_user_uidb64,
|
||||||
context={'token': self.change_email_token,
|
'site_name': settings.SITE_NAME}
|
||||||
'uidb64': self.get_user_uidb64,
|
|
||||||
'domain_uri': settings.DOMAIN_URI,
|
|
||||||
'site_name': settings.SITE_NAME})
|
|
||||||
|
|
||||||
|
|
||||||
class ResetPasswordTokenQuerySet(models.QuerySet):
|
|
||||||
"""Reset password token query set"""
|
|
||||||
|
|
||||||
def expired(self):
|
|
||||||
"""Show only expired"""
|
|
||||||
return self.filter(expiry_datetime__lt=timezone.now())
|
|
||||||
|
|
||||||
def valid(self):
|
|
||||||
"""Show only valid"""
|
|
||||||
return self.filter(expiry_datetime__gt=timezone.now())
|
|
||||||
|
|
||||||
def by_user(self, user):
|
|
||||||
"""Show obj by user"""
|
|
||||||
return self.filter(user=user)
|
|
||||||
|
|
||||||
|
|
||||||
class ResetPasswordToken(PlatformMixin, ProjectBaseMixin):
|
|
||||||
"""Reset password model"""
|
|
||||||
|
|
||||||
user = models.ForeignKey(User,
|
|
||||||
related_name='password_reset_tokens',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
verbose_name=_('The User which is associated to '
|
|
||||||
'this password reset token'))
|
|
||||||
# Key field, though it is not the primary key of the model
|
|
||||||
key = models.CharField(max_length=255,
|
|
||||||
verbose_name=_('Key'))
|
|
||||||
|
|
||||||
ip_address = models.GenericIPAddressField(default='',
|
|
||||||
blank=True, null=True,
|
|
||||||
verbose_name=_('The IP address of this session'))
|
|
||||||
|
|
||||||
expiry_datetime = models.DateTimeField(blank=True, null=True,
|
|
||||||
verbose_name=_('Expiration datetime'))
|
|
||||||
|
|
||||||
objects = ResetPasswordTokenQuerySet.as_manager()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _("Password Reset Token")
|
|
||||||
verbose_name_plural = _("Password Reset Tokens")
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "Password reset token for user {user}".format(user=self.user)
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
"""Override save method"""
|
|
||||||
if not self.expiry_datetime:
|
|
||||||
self.expiry_datetime = (
|
|
||||||
timezone.now() +
|
|
||||||
timezone.timedelta(hours=self.get_resetting_token_expiration)
|
|
||||||
)
|
|
||||||
if not self.key:
|
|
||||||
self.key = self.generate_token
|
|
||||||
return super(ResetPasswordToken, self).save(*args, **kwargs)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def get_resetting_token_expiration(self):
|
|
||||||
"""Get resetting token expiration"""
|
|
||||||
return settings.RESETTING_TOKEN_EXPIRATION
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_valid(self):
|
|
||||||
"""Check if valid token or not"""
|
|
||||||
return timezone.now() > self.expiry_datetime
|
|
||||||
|
|
||||||
@property
|
|
||||||
def generate_token(self):
|
|
||||||
"""Generates a pseudo random code"""
|
|
||||||
return password_token_generator.make_token(self.user)
|
|
||||||
|
|
||||||
def reset_password_template(self, country_code):
|
def reset_password_template(self, country_code):
|
||||||
"""Get reset password template"""
|
"""Get reset password template"""
|
||||||
|
context = {'token': self.reset_password_token,
|
||||||
|
'country_code': country_code}
|
||||||
|
context.update(self.base_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=context)
|
||||||
'uidb64': self.user.get_user_uidb64,
|
|
||||||
'domain_uri': settings.DOMAIN_URI,
|
|
||||||
'site_name': settings.SITE_NAME,
|
|
||||||
'country_code': country_code})
|
|
||||||
|
|
||||||
@staticmethod
|
def confirm_email_template(self, country_code):
|
||||||
def token_is_valid(user, token):
|
"""Get confirm email template"""
|
||||||
"""Check if token is valid"""
|
context = {'token': self.confirm_email_token,
|
||||||
return password_token_generator.check_token(user, token)
|
'country_code': country_code}
|
||||||
|
context.update(self.base_template)
|
||||||
|
return render_to_string(
|
||||||
|
template_name=settings.CONFIRM_EMAIL_TEMPLATE,
|
||||||
|
context=context)
|
||||||
|
|
||||||
def overdue(self):
|
def change_email_template(self, country_code):
|
||||||
"""Overdue instance"""
|
"""Get change email template"""
|
||||||
self.expiry_datetime = timezone.now()
|
context = {'token': self.change_email_token,
|
||||||
self.save()
|
'country_code': country_code}
|
||||||
|
context.update(self.base_template)
|
||||||
|
return render_to_string(
|
||||||
|
template_name=settings.CHANGE_EMAIL_TEMPLATE,
|
||||||
|
context=context)
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class UserSerializer(serializers.ModelSerializer):
|
||||||
def validate_email(self, value):
|
def validate_email(self, value):
|
||||||
"""Validate email value"""
|
"""Validate email value"""
|
||||||
if value == self.instance.email:
|
if value == self.instance.email:
|
||||||
raise serializers.ValidationError()
|
raise serializers.ValidationError(detail='Equal email address.')
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def validate_username(self, value):
|
def validate_username(self, value):
|
||||||
|
|
@ -58,24 +58,21 @@ class UserSerializer(serializers.ModelSerializer):
|
||||||
raise utils_exceptions.NotValidUsernameError()
|
raise utils_exceptions.NotValidUsernameError()
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def validate(self, attrs):
|
|
||||||
if ('cropped_image' in attrs or 'image' in attrs) and \
|
|
||||||
('cropped_image' not in attrs or 'image' not in attrs):
|
|
||||||
raise utils_exceptions.UserUpdateUploadImageError()
|
|
||||||
return attrs
|
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
"""
|
"""Override update method"""
|
||||||
Override update method
|
|
||||||
"""
|
|
||||||
if 'email' in validated_data:
|
|
||||||
validated_data['email_confirmed'] = False
|
|
||||||
instance = super().update(instance, validated_data)
|
instance = super().update(instance, validated_data)
|
||||||
# Send verification link on user email for change email address
|
if 'email' in validated_data:
|
||||||
if settings.USE_CELERY:
|
instance.email_confirmed = False
|
||||||
tasks.confirm_new_email_address.delay(instance.id)
|
instance.save()
|
||||||
else:
|
# Send verification link on user email for change email address
|
||||||
tasks.confirm_new_email_address(instance.id)
|
if settings.USE_CELERY:
|
||||||
|
tasks.change_email_address.delay(
|
||||||
|
user_id=instance.id,
|
||||||
|
country_code=self.context.get('request').country_code)
|
||||||
|
else:
|
||||||
|
tasks.change_email_address(
|
||||||
|
user_id=instance.id,
|
||||||
|
country_code=self.context.get('request').country_code)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -163,7 +160,7 @@ class ConfirmEmailSerializer(serializers.ModelSerializer):
|
||||||
"""Override validate method"""
|
"""Override validate method"""
|
||||||
email_confirmed = self.instance.email_confirmed
|
email_confirmed = self.instance.email_confirmed
|
||||||
if email_confirmed:
|
if email_confirmed:
|
||||||
raise serializers.ValidationError()
|
raise utils_exceptions.EmailConfirmedError()
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,18 @@
|
||||||
"""Serializers for account web"""
|
"""Serializers for account web"""
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.auth import password_validation as password_validators
|
from django.contrib.auth import password_validation as password_validators
|
||||||
from django.db.models import Q
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from account import models, tasks
|
from account import models
|
||||||
from utils import exceptions as utils_exceptions
|
from utils import exceptions as utils_exceptions
|
||||||
from utils.methods import username_validator
|
from utils.methods import username_validator
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetSerializer(serializers.ModelSerializer):
|
class PasswordResetSerializer(serializers.Serializer):
|
||||||
"""Serializer from model PasswordReset"""
|
"""Serializer from model PasswordReset"""
|
||||||
username_or_email = serializers.CharField(required=False,
|
username_or_email = serializers.CharField(required=False,
|
||||||
write_only=True,)
|
write_only=True,)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Meta class"""
|
|
||||||
model = models.ResetPasswordToken
|
|
||||||
fields = (
|
|
||||||
'username_or_email',
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def request(self):
|
def request(self):
|
||||||
"""Get request from context"""
|
"""Get request from context"""
|
||||||
|
|
@ -30,41 +21,29 @@ class PasswordResetSerializer(serializers.ModelSerializer):
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
"""Override validate method"""
|
"""Override validate method"""
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
|
username_or_email = attrs.get('username_or_email')
|
||||||
|
|
||||||
if user.is_anonymous:
|
if not user.is_authenticated:
|
||||||
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
|
|
||||||
filters = {}
|
filters = {}
|
||||||
if username_validator(username_or_email):
|
if username_validator(username_or_email):
|
||||||
filters.update({'username': username_or_email})
|
filters.update({'username__icontains': username_or_email})
|
||||||
else:
|
else:
|
||||||
filters.update({'email': username_or_email.lower()})
|
filters.update({'email__icontains': username_or_email})
|
||||||
user_qs = models.User.objects.filter(**filters)
|
|
||||||
if user_qs.exists() and filters:
|
if filters:
|
||||||
attrs['user'] = user_qs.first()
|
filters.update({'is_active': True})
|
||||||
else:
|
user_qs = models.User.objects.filter(**filters)
|
||||||
attrs['user'] = user
|
|
||||||
|
if not user_qs.exists():
|
||||||
|
raise utils_exceptions.UserNotFoundError()
|
||||||
|
user = user_qs.first()
|
||||||
|
|
||||||
|
attrs['user'] = user
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def create(self, validated_data, *args, **kwargs):
|
|
||||||
"""Override create method"""
|
|
||||||
user = validated_data.pop('user')
|
|
||||||
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(request_id=obj.id,
|
|
||||||
country_code=self.request.country_code)
|
|
||||||
else:
|
|
||||||
tasks.send_reset_password_email(request_id=obj.id,
|
|
||||||
country_code=self.request.country_code)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetConfirmSerializer(serializers.ModelSerializer):
|
class PasswordResetConfirmSerializer(serializers.ModelSerializer):
|
||||||
"""Serializer for model User"""
|
"""Serializer for model User"""
|
||||||
|
|
@ -73,30 +52,24 @@ class PasswordResetConfirmSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Meta class"""
|
"""Meta class"""
|
||||||
model = models.ResetPasswordToken
|
model = models.User
|
||||||
fields = ('password', )
|
fields = ('password', )
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate_password(self, value):
|
||||||
"""Override validate method"""
|
"""Password validation method."""
|
||||||
user = self.instance.user
|
|
||||||
password = attrs.get('password')
|
|
||||||
try:
|
try:
|
||||||
# Compare new password with the old ones
|
# Compare new password with the old ones
|
||||||
if user.check_password(raw_password=password):
|
if self.instance.check_password(raw_password=value):
|
||||||
raise utils_exceptions.PasswordsAreEqual()
|
raise utils_exceptions.PasswordsAreEqual()
|
||||||
# Validate password
|
# Validate password
|
||||||
password_validators.validate_password(password=password)
|
password_validators.validate_password(password=value)
|
||||||
except serializers.ValidationError as e:
|
except serializers.ValidationError as e:
|
||||||
raise serializers.ValidationError(str(e))
|
raise serializers.ValidationError(str(e))
|
||||||
else:
|
return value
|
||||||
return attrs
|
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
"""Override update method"""
|
"""Override update method"""
|
||||||
# Update user password from instance
|
# Update user password from instance
|
||||||
instance.user.set_password(validated_data.get('password'))
|
instance.set_password(validated_data.get('password'))
|
||||||
instance.user.save()
|
instance.save()
|
||||||
|
|
||||||
# Overdue instance
|
|
||||||
instance.overdue()
|
|
||||||
return instance
|
return instance
|
||||||
|
|
|
||||||
|
|
@ -11,26 +11,37 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def send_reset_password_email(request_id, country_code):
|
def send_reset_password_email(user_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)
|
user = models.User.objects.get(id=user_id)
|
||||||
user = obj.user
|
|
||||||
user.send_email(subject=_('Password resetting'),
|
user.send_email(subject=_('Password resetting'),
|
||||||
message=obj.reset_password_template(country_code))
|
message=user.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 reset password: '
|
||||||
f'{request_id}')
|
f'{user_id}')
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def confirm_new_email_address(user_id):
|
def confirm_new_email_address(user_id, country_code):
|
||||||
"""Send email to user new email."""
|
"""Send email to user new email."""
|
||||||
try:
|
try:
|
||||||
user = models.User.objects.get(id=user_id)
|
user = models.User.objects.get(id=user_id)
|
||||||
user.send_email(subject=_('Validate new email address'),
|
user.send_email(subject=_('Validate new email address'),
|
||||||
message=user.change_email_template)
|
message=user.confirm_email_template(country_code))
|
||||||
except:
|
except:
|
||||||
logger.error(f'METHOD_NAME: {confirm_new_email_address.__name__}\n'
|
logger.error(f'METHOD_NAME: {confirm_new_email_address.__name__}\n'
|
||||||
f'DETAIL: Exception occurred for user: {user_id}')
|
f'DETAIL: Exception occurred for user: {user_id}')
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def change_email_address(user_id, country_code):
|
||||||
|
"""Send email to user new email."""
|
||||||
|
try:
|
||||||
|
user = models.User.objects.get(id=user_id)
|
||||||
|
user.send_email(subject=_('Validate new email address'),
|
||||||
|
message=user.change_email_template(country_code))
|
||||||
|
except:
|
||||||
|
logger.error(f'METHOD_NAME: {change_email_address.__name__}\n'
|
||||||
|
f'DETAIL: Exception occurred for user: {user_id}')
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,5 @@ app_name = 'account'
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('user/', views.UserRetrieveUpdateView.as_view(), name='user-retrieve-update'),
|
path('user/', views.UserRetrieveUpdateView.as_view(), name='user-retrieve-update'),
|
||||||
path('change-password/', views.ChangePasswordView.as_view(), name='change-password'),
|
path('change-password/', views.ChangePasswordView.as_view(), name='change-password'),
|
||||||
path('change-email/confirm/<uidb64>/<token>/', views.ChangeEmailConfirmView.as_view(),
|
path('email/confirm/<uidb64>/<token>/', views.ConfirmEmailView.as_view(), name='confirm-email'),
|
||||||
name='change-email-confirm'),
|
|
||||||
path('confirm-email/', views.ConfirmEmailView.as_view(), name='confirm-email'),
|
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,8 @@ app_name = 'account'
|
||||||
|
|
||||||
urlpatterns_api = [
|
urlpatterns_api = [
|
||||||
path('reset-password/', views.PasswordResetView.as_view(), name='password-reset'),
|
path('reset-password/', views.PasswordResetView.as_view(), name='password-reset'),
|
||||||
path('form/reset-password/<uidb64>/<token>/', views.FormPasswordResetConfirmView.as_view(),
|
path('reset-password/confirm/<uidb64>/<token>/', views.PasswordResetConfirmView.as_view(),
|
||||||
name='form-password-reset-confirm'),
|
name='password-reset-confirm'),
|
||||||
path('form/reset-password/success/', views.FormPasswordResetSuccessView.as_view(),
|
|
||||||
name='form-password-reset-success'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns = urlpatterns_api + \
|
urlpatterns = urlpatterns_api + \
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,12 @@ 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
|
||||||
from utils import exceptions as utils_exceptions
|
from utils import exceptions as utils_exceptions
|
||||||
from utils.models import GMTokenGenerator
|
from utils.models import GMTokenGenerator
|
||||||
from utils.views import (JWTUpdateAPIView,
|
from utils.views import JWTGenericViewMixin
|
||||||
JWTGenericViewMixin)
|
|
||||||
|
|
||||||
|
|
||||||
# User views
|
# User views
|
||||||
|
|
@ -26,53 +24,27 @@ class UserRetrieveUpdateView(generics.RetrieveUpdateAPIView):
|
||||||
return self.request.user
|
return self.request.user
|
||||||
|
|
||||||
|
|
||||||
class ChangePasswordView(JWTUpdateAPIView):
|
class ChangePasswordView(generics.GenericAPIView):
|
||||||
"""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:
|
|
||||||
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()
|
|
||||||
if not password_reset_obj.user.is_active:
|
|
||||||
raise utils_exceptions.UserNotFoundError()
|
|
||||||
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"""
|
||||||
instance = self.get_object()
|
serializer = self.get_serializer(instance=self.request.user,
|
||||||
serializer = self.get_serializer(instance=instance,
|
|
||||||
data=request.data)
|
data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return Response(status=status.HTTP_200_OK)
|
return Response(status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
class ConfirmEmailView(JWTGenericViewMixin):
|
class SendConfirmationEmailView(JWTGenericViewMixin):
|
||||||
"""Confirm email view."""
|
"""Confirm email view."""
|
||||||
serializer_class = serializers.ConfirmEmailSerializer
|
serializer_class = serializers.ConfirmEmailSerializer
|
||||||
queryset = models.User.objects.all()
|
queryset = models.User.objects.all()
|
||||||
|
|
||||||
def patch(self, request, *args, **kwargs):
|
def patch(self, request, *args, **kwargs):
|
||||||
"""Implement POST-method"""
|
"""Implement PATCH-method"""
|
||||||
# Get user instance
|
# Get user instance
|
||||||
instance = self.request.user
|
instance = self.request.user
|
||||||
|
|
||||||
|
|
@ -82,7 +54,7 @@ class ConfirmEmailView(JWTGenericViewMixin):
|
||||||
return Response(status=status.HTTP_200_OK)
|
return Response(status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
class ChangeEmailConfirmView(JWTGenericViewMixin):
|
class ConfirmEmailView(JWTGenericViewMixin):
|
||||||
"""View for confirm changing email"""
|
"""View for confirm changing email"""
|
||||||
|
|
||||||
permission_classes = (permissions.AllowAny,)
|
permission_classes = (permissions.AllowAny,)
|
||||||
|
|
@ -95,12 +67,18 @@ class ChangeEmailConfirmView(JWTGenericViewMixin):
|
||||||
user_qs = models.User.objects.filter(pk=uid)
|
user_qs = models.User.objects.filter(pk=uid)
|
||||||
if user_qs.exists():
|
if user_qs.exists():
|
||||||
user = user_qs.first()
|
user = user_qs.first()
|
||||||
if not GMTokenGenerator(GMTokenGenerator.CHANGE_EMAIL).check_token(
|
if not GMTokenGenerator(GMTokenGenerator.CONFIRM_EMAIL).check_token(
|
||||||
user, token):
|
user, token):
|
||||||
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)
|
# 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(status=status.HTTP_200_OK))
|
||||||
else:
|
else:
|
||||||
raise utils_exceptions.UserNotFoundError()
|
raise utils_exceptions.UserNotFoundError()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,35 @@
|
||||||
"""Web account views"""
|
"""Web account views"""
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.tokens import default_token_generator
|
from django.contrib.auth.tokens import default_token_generator as password_token_generator
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from django.http import HttpResponseRedirect
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.urls import reverse_lazy
|
|
||||||
from django.utils.decorators import method_decorator
|
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.http import urlsafe_base64_decode
|
from django.utils.http import urlsafe_base64_decode
|
||||||
from django.utils.translation import gettext_lazy as _
|
from rest_framework import permissions, status, generics
|
||||||
from django.views.decorators.cache import never_cache
|
|
||||||
from django.views.decorators.debug import sensitive_post_parameters
|
|
||||||
from django.views.generic.edit import FormView
|
|
||||||
from rest_framework import permissions
|
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework import views
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from account import models
|
from account import tasks, models
|
||||||
from account.forms import SetPasswordForm
|
|
||||||
from account.serializers import web as serializers
|
from account.serializers import web as serializers
|
||||||
from utils import exceptions as utils_exceptions
|
from utils import exceptions as utils_exceptions
|
||||||
from utils.models import GMTokenGenerator
|
|
||||||
from utils.views import JWTGenericViewMixin
|
from utils.views import JWTGenericViewMixin
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetView(JWTGenericViewMixin):
|
class PasswordResetView(generics.GenericAPIView):
|
||||||
"""View for resetting user password"""
|
"""View for resetting user password"""
|
||||||
permission_classes = (permissions.AllowAny, )
|
permission_classes = (permissions.AllowAny, )
|
||||||
serializer_class = serializers.PasswordResetSerializer
|
serializer_class = serializers.PasswordResetSerializer
|
||||||
queryset = models.ResetPasswordToken.objects.valid()
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
"""Override create method"""
|
"""Override create method"""
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
if serializer.validated_data.get('user'):
|
if serializer.validated_data.get('user'):
|
||||||
serializer.save()
|
user = serializer.validated_data.pop('user')
|
||||||
|
if settings.USE_CELERY:
|
||||||
|
tasks.send_reset_password_email.delay(user_id=user.id,
|
||||||
|
country_code=self.request.country_code)
|
||||||
|
else:
|
||||||
|
tasks.send_reset_password_email(user_id=user.id,
|
||||||
|
country_code=self.request.country_code)
|
||||||
return Response(status=status.HTTP_200_OK)
|
return Response(status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -44,10 +37,7 @@ class PasswordResetConfirmView(JWTGenericViewMixin):
|
||||||
"""View for confirmation new password"""
|
"""View for confirmation new password"""
|
||||||
serializer_class = serializers.PasswordResetConfirmSerializer
|
serializer_class = serializers.PasswordResetConfirmSerializer
|
||||||
permission_classes = (permissions.AllowAny,)
|
permission_classes = (permissions.AllowAny,)
|
||||||
|
queryset = models.User.objects.active()
|
||||||
def get_queryset(self):
|
|
||||||
"""Override get_queryset method"""
|
|
||||||
return models.ResetPasswordToken.objects.valid()
|
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
"""Override get_object method
|
"""Override get_object method
|
||||||
|
|
@ -58,128 +48,27 @@ class PasswordResetConfirmView(JWTGenericViewMixin):
|
||||||
user_id = force_text(urlsafe_base64_decode(uidb64))
|
user_id = force_text(urlsafe_base64_decode(uidb64))
|
||||||
token = self.kwargs.get('token')
|
token = self.kwargs.get('token')
|
||||||
|
|
||||||
filter_kwargs = {'key': token, 'user_id': user_id}
|
obj = get_object_or_404(queryset, id=user_id)
|
||||||
obj = get_object_or_404(queryset, **filter_kwargs)
|
|
||||||
|
|
||||||
if not GMTokenGenerator(GMTokenGenerator.RESET_PASSWORD).check_token(
|
if not password_token_generator.check_token(user=obj, token=token):
|
||||||
user=obj.user, token=token):
|
raise utils_exceptions.NotValidTokenError()
|
||||||
raise utils_exceptions.NotValidAccessTokenError()
|
|
||||||
|
|
||||||
# May raise a permission denied
|
# May raise a permission denied
|
||||||
self.check_object_permissions(self.request, obj)
|
self.check_object_permissions(self.request, obj)
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def put(self, request, *args, **kwargs):
|
def patch(self, request, *args, **kwargs):
|
||||||
"""Implement PUT method"""
|
"""Implement PATCH method"""
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
serializer = self.get_serializer(instance=instance,
|
serializer = self.get_serializer(instance=instance,
|
||||||
data=request.data)
|
data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return Response(status=status.HTTP_200_OK)
|
# Create tokens
|
||||||
|
tokens = instance.create_jwt_tokens()
|
||||||
|
return self._put_cookies_in_response(
|
||||||
# Form view
|
cookies=self._put_data_in_cookies(
|
||||||
class PasswordContextMixin:
|
access_token=tokens.get('access_token'),
|
||||||
extra_context = None
|
refresh_token=tokens.get('refresh_token')),
|
||||||
|
response=Response(status=status.HTTP_200_OK))
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context.update({
|
|
||||||
'title': self.title,
|
|
||||||
**(self.extra_context or {})
|
|
||||||
})
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class FormPasswordResetSuccessView(views.APIView):
|
|
||||||
"""View for successful reset password"""
|
|
||||||
|
|
||||||
permission_classes = (permissions.AllowAny, )
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
"""Implement GET-method"""
|
|
||||||
return Response(status=status.HTTP_200_OK)
|
|
||||||
|
|
||||||
|
|
||||||
class FormPasswordResetConfirmView(PasswordContextMixin, FormView):
|
|
||||||
|
|
||||||
INTERNAL_RESET_URL_TOKEN = 'set-password'
|
|
||||||
INTERNAL_RESET_SESSION_TOKEN = '_password_reset_token'
|
|
||||||
|
|
||||||
form_class = SetPasswordForm
|
|
||||||
post_reset_login = False
|
|
||||||
post_reset_login_backend = None
|
|
||||||
success_url = reverse_lazy('web:account:form-password-reset-success')
|
|
||||||
template_name = settings.CONFIRMATION_PASSWORD_RESET_TEMPLATE
|
|
||||||
title = _('Enter new password')
|
|
||||||
token_generator = default_token_generator
|
|
||||||
|
|
||||||
@method_decorator(sensitive_post_parameters())
|
|
||||||
@method_decorator(never_cache)
|
|
||||||
def dispatch(self, *args, **kwargs):
|
|
||||||
assert 'uidb64' in kwargs and 'token' in kwargs
|
|
||||||
|
|
||||||
self.validlink = False
|
|
||||||
self.user = self.get_user(kwargs['uidb64'])
|
|
||||||
|
|
||||||
if self.user is not None:
|
|
||||||
token = kwargs['token']
|
|
||||||
if token == self.INTERNAL_RESET_URL_TOKEN:
|
|
||||||
session_token = self.request.session.get(self.INTERNAL_RESET_SESSION_TOKEN)
|
|
||||||
if self.token_generator.check_token(self.user, session_token):
|
|
||||||
# If the token is valid, display the password reset form.
|
|
||||||
self.validlink = True
|
|
||||||
return super().dispatch(*args, **kwargs)
|
|
||||||
else:
|
|
||||||
if self.token_generator.check_token(self.user, token):
|
|
||||||
# Store the token in the session and redirect to the
|
|
||||||
# password reset form at a URL without the token. That
|
|
||||||
# avoids the possibility of leaking the token in the
|
|
||||||
# HTTP Referer header.
|
|
||||||
self.request.session[self.INTERNAL_RESET_SESSION_TOKEN] = token
|
|
||||||
redirect_url = self.request.path.replace(token, self.INTERNAL_RESET_URL_TOKEN)
|
|
||||||
return HttpResponseRedirect(redirect_url)
|
|
||||||
|
|
||||||
# Display the "Password reset unsuccessful" page.
|
|
||||||
return self.render_to_response(self.get_context_data())
|
|
||||||
|
|
||||||
def get_user(self, uidb64):
|
|
||||||
try:
|
|
||||||
# urlsafe_base64_decode() decodes to bytestring
|
|
||||||
uid = urlsafe_base64_decode(uidb64).decode()
|
|
||||||
user = models.User.objects.get(pk=uid)
|
|
||||||
except (TypeError, ValueError, OverflowError, models.User.DoesNotExist, ValidationError):
|
|
||||||
user = None
|
|
||||||
return user
|
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
|
||||||
kwargs = super().get_form_kwargs()
|
|
||||||
kwargs['user'] = self.user
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
# Saving form
|
|
||||||
form.save()
|
|
||||||
user = form.user
|
|
||||||
|
|
||||||
# Expire user tokens
|
|
||||||
user.expire_access_tokens()
|
|
||||||
user.expire_refresh_tokens()
|
|
||||||
|
|
||||||
# Pop session token
|
|
||||||
del self.request.session[self.INTERNAL_RESET_SESSION_TOKEN]
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
if self.validlink:
|
|
||||||
context['validlink'] = True
|
|
||||||
else:
|
|
||||||
context.update({
|
|
||||||
'form': None,
|
|
||||||
'title': _('Password reset unsuccessful'),
|
|
||||||
'validlink': False,
|
|
||||||
})
|
|
||||||
return context
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ urlpatterns_oauth2 = [
|
||||||
|
|
||||||
urlpatterns_jwt = [
|
urlpatterns_jwt = [
|
||||||
path('signup/', views.SignUpView.as_view(), name='signup'),
|
path('signup/', views.SignUpView.as_view(), name='signup'),
|
||||||
path('signup/confirm/<uidb64>/<token>/', views.VerifyEmailConfirmView.as_view(),
|
path('signup/confirm/<uidb64>/<token>/', views.ConfirmationEmailView.as_view(),
|
||||||
name='signup-confirm'),
|
name='signup-confirm'),
|
||||||
path('login/', views.LoginByUsernameOrEmailView.as_view(), name='login'),
|
path('login/', views.LoginByUsernameOrEmailView.as_view(), name='login'),
|
||||||
path('logout/', views.LogoutView.as_view(), name="logout"),
|
path('logout/', views.LogoutView.as_view(), name="logout"),
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,12 @@ from authorization.serializers import common as serializers
|
||||||
from utils import exceptions as utils_exceptions
|
from utils import exceptions as utils_exceptions
|
||||||
from utils.models import GMTokenGenerator
|
from utils.models import GMTokenGenerator
|
||||||
from utils.permissions import IsAuthenticatedAndTokenIsValid
|
from utils.permissions import IsAuthenticatedAndTokenIsValid
|
||||||
from utils.views import (JWTGenericViewMixin,
|
from utils.views import JWTGenericViewMixin
|
||||||
JWTCreateAPIView)
|
|
||||||
|
|
||||||
|
|
||||||
# Mixins
|
# Mixins
|
||||||
# JWTAuthView mixin
|
# JWTAuthView mixin
|
||||||
class JWTAuthViewMixin(JWTCreateAPIView):
|
class JWTAuthViewMixin(JWTGenericViewMixin):
|
||||||
"""Mixin for authentication views"""
|
"""Mixin for authentication views"""
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
@ -151,7 +150,7 @@ class OAuth2SignUpView(OAuth2ViewMixin, JWTGenericViewMixin):
|
||||||
|
|
||||||
# JWT
|
# JWT
|
||||||
# Sign in via username and password
|
# Sign in via username and password
|
||||||
class SignUpView(JWTCreateAPIView):
|
class SignUpView(generics.GenericAPIView):
|
||||||
"""View for classic signup"""
|
"""View for classic signup"""
|
||||||
permission_classes = (permissions.AllowAny, )
|
permission_classes = (permissions.AllowAny, )
|
||||||
serializer_class = serializers.SignupSerializer
|
serializer_class = serializers.SignupSerializer
|
||||||
|
|
@ -164,7 +163,7 @@ class SignUpView(JWTCreateAPIView):
|
||||||
return Response(status=status.HTTP_201_CREATED)
|
return Response(status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
|
||||||
class VerifyEmailConfirmView(JWTGenericViewMixin):
|
class ConfirmationEmailView(JWTGenericViewMixin):
|
||||||
"""View for confirmation email"""
|
"""View for confirmation email"""
|
||||||
|
|
||||||
permission_classes = (permissions.AllowAny, )
|
permission_classes = (permissions.AllowAny, )
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
"""News app common app."""
|
"""News app common app."""
|
||||||
from rest_framework import generics, permissions
|
from rest_framework import generics, permissions
|
||||||
|
|
||||||
from news import filters, models
|
from news import filters, models
|
||||||
from news.serializers import common as serializers
|
from news.serializers import common as serializers
|
||||||
from utils.views import JWTGenericViewMixin, JWTListAPIView
|
from utils.views import JWTGenericViewMixin
|
||||||
|
|
||||||
|
|
||||||
class NewsMixin:
|
class NewsMixin:
|
||||||
|
|
@ -18,7 +19,7 @@ class NewsMixin:
|
||||||
.order_by('-is_highlighted', '-created')
|
.order_by('-is_highlighted', '-created')
|
||||||
|
|
||||||
|
|
||||||
class NewsListView(NewsMixin, JWTListAPIView):
|
class NewsListView(NewsMixin, generics.ListAPIView):
|
||||||
"""News list view."""
|
"""News list view."""
|
||||||
|
|
||||||
filter_class = filters.NewsListFilterSet
|
filter_class = filters.NewsListFilterSet
|
||||||
|
|
|
||||||
|
|
@ -32,15 +32,6 @@ class UserNotFoundError(AuthErrorMixin, ProjectBaseException):
|
||||||
default_detail = _('User not found')
|
default_detail = _('User not found')
|
||||||
|
|
||||||
|
|
||||||
class PasswordRequestResetExists(ProjectBaseException):
|
|
||||||
"""
|
|
||||||
The exception should be thrown when request for reset password
|
|
||||||
is already exists and valid
|
|
||||||
"""
|
|
||||||
status_code = status.HTTP_400_BAD_REQUEST
|
|
||||||
default_detail = _('Password request is already exists. Please wait.')
|
|
||||||
|
|
||||||
|
|
||||||
class EmailSendingError(exceptions.APIException):
|
class EmailSendingError(exceptions.APIException):
|
||||||
"""The exception should be thrown when unable to send an email"""
|
"""The exception should be thrown when unable to send an email"""
|
||||||
status_code = status.HTTP_400_BAD_REQUEST
|
status_code = status.HTTP_400_BAD_REQUEST
|
||||||
|
|
@ -142,3 +133,12 @@ class FavoritesError(exceptions.APIException):
|
||||||
"""
|
"""
|
||||||
status_code = status.HTTP_400_BAD_REQUEST
|
status_code = status.HTTP_400_BAD_REQUEST
|
||||||
default_detail = _('Item is already in favorites.')
|
default_detail = _('Item is already in favorites.')
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordResetRequestExistedError(exceptions.APIException):
|
||||||
|
"""
|
||||||
|
The exception should be thrown when password reset request
|
||||||
|
already exists and valid.
|
||||||
|
"""
|
||||||
|
status_code = status.HTTP_400_BAD_REQUEST
|
||||||
|
default_detail = _('Password reset request is already exists and valid.')
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from rest_framework.response import Response
|
||||||
|
|
||||||
|
|
||||||
# JWT
|
# JWT
|
||||||
# Login base view mixin
|
# Login base view mixins
|
||||||
class JWTGenericViewMixin(generics.GenericAPIView):
|
class JWTGenericViewMixin(generics.GenericAPIView):
|
||||||
"""JWT view mixin"""
|
"""JWT view mixin"""
|
||||||
|
|
||||||
|
|
@ -95,102 +95,3 @@ class JWTGenericViewMixin(generics.GenericAPIView):
|
||||||
http_only=self.REFRESH_TOKEN_HTTP_ONLY,
|
http_only=self.REFRESH_TOKEN_HTTP_ONLY,
|
||||||
secure=self.REFRESH_TOKEN_SECURE,
|
secure=self.REFRESH_TOKEN_SECURE,
|
||||||
max_age=_cookies.get('max_age'))]
|
max_age=_cookies.get('max_age'))]
|
||||||
|
|
||||||
|
|
||||||
class JWTListAPIView(JWTGenericViewMixin, generics.ListAPIView):
|
|
||||||
"""
|
|
||||||
Concrete view for creating a model instance.
|
|
||||||
"""
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
queryset = self.filter_queryset(self.get_queryset())
|
|
||||||
page = self.paginate_queryset(queryset)
|
|
||||||
if page is not None:
|
|
||||||
serializer = self.get_serializer(page, many=True)
|
|
||||||
response = self.get_paginated_response(serializer.data)
|
|
||||||
else:
|
|
||||||
serializer = self.get_serializer(queryset, many=True)
|
|
||||||
response = Response(serializer.data)
|
|
||||||
access_token, refresh_token = self._get_tokens_from_cookies(request)
|
|
||||||
return self._put_cookies_in_response(
|
|
||||||
cookies=self._put_data_in_cookies(access_token=access_token.value,
|
|
||||||
refresh_token=refresh_token.value),
|
|
||||||
response=response)
|
|
||||||
|
|
||||||
|
|
||||||
class JWTCreateAPIView(JWTGenericViewMixin, generics.CreateAPIView):
|
|
||||||
"""
|
|
||||||
Concrete view for creating a model instance.
|
|
||||||
"""
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
serializer = self.get_serializer(data=request.data)
|
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
serializer.save()
|
|
||||||
response = Response(serializer.data, status=status.HTTP_201_CREATED)
|
|
||||||
access_token, refresh_token = self._get_tokens_from_cookies(request)
|
|
||||||
return self._put_cookies_in_response(
|
|
||||||
cookies=self._put_data_in_cookies(access_token=access_token.value,
|
|
||||||
refresh_token=refresh_token.value),
|
|
||||||
response=response)
|
|
||||||
|
|
||||||
|
|
||||||
class JWTRetrieveAPIView(JWTGenericViewMixin, generics.RetrieveAPIView):
|
|
||||||
"""
|
|
||||||
Concrete view for retrieving a model instance.
|
|
||||||
"""
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
"""Implement GET method"""
|
|
||||||
queryset = self.filter_queryset(self.get_queryset())
|
|
||||||
page = self.paginate_queryset(queryset)
|
|
||||||
if page is not None:
|
|
||||||
serializer = self.get_serializer(page, many=True)
|
|
||||||
response = self.get_paginated_response(serializer.data)
|
|
||||||
else:
|
|
||||||
serializer = self.get_serializer(queryset, many=True)
|
|
||||||
response = Response(serializer.data, status.HTTP_200_OK)
|
|
||||||
access_token, refresh_token = self._get_tokens_from_cookies(request)
|
|
||||||
return self._put_cookies_in_response(
|
|
||||||
cookies=self._put_data_in_cookies(access_token=access_token,
|
|
||||||
refresh_token=refresh_token),
|
|
||||||
response=response)
|
|
||||||
|
|
||||||
|
|
||||||
class JWTDestroyAPIView(JWTGenericViewMixin, generics.DestroyAPIView):
|
|
||||||
"""
|
|
||||||
Concrete view for deleting a model instance.
|
|
||||||
"""
|
|
||||||
def delete(self, request, *args, **kwargs):
|
|
||||||
instance = self.get_object()
|
|
||||||
instance.delete()
|
|
||||||
response = Response(status=status.HTTP_204_NO_CONTENT)
|
|
||||||
access_token, refresh_token = self._get_tokens_from_cookies(request)
|
|
||||||
return self._put_cookies_in_response(
|
|
||||||
cookies=self._put_data_in_cookies(access_token=access_token,
|
|
||||||
refresh_token=refresh_token),
|
|
||||||
response=response)
|
|
||||||
|
|
||||||
|
|
||||||
class JWTUpdateAPIView(JWTGenericViewMixin, generics.UpdateAPIView):
|
|
||||||
"""
|
|
||||||
Concrete view for updating a model instance.
|
|
||||||
"""
|
|
||||||
def put(self, request, *args, **kwargs):
|
|
||||||
partial = kwargs.pop('partial', False)
|
|
||||||
instance = self.get_object()
|
|
||||||
serializer = self.get_serializer(instance, data=request.data, partial=partial)
|
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
serializer.save()
|
|
||||||
if getattr(instance, '_prefetched_objects_cache', None):
|
|
||||||
# If 'prefetch_related' has been applied to a queryset, we need to
|
|
||||||
# forcibly invalidate the prefetch cache on the instance.
|
|
||||||
instance._prefetched_objects_cache = {}
|
|
||||||
response = Response(serializer.data)
|
|
||||||
access_token, refresh_token = self._get_tokens_from_cookies(request)
|
|
||||||
return self._put_cookies_in_response(
|
|
||||||
cookies=self._put_data_in_cookies(access_token=access_token,
|
|
||||||
refresh_token=refresh_token),
|
|
||||||
response=response)
|
|
||||||
|
|
||||||
def patch(self, request, *args, **kwargs):
|
|
||||||
kwargs['partial'] = True
|
|
||||||
return self.put(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -369,7 +369,6 @@ PASSWORD_RESET_TIMEOUT_DAYS = 1
|
||||||
|
|
||||||
|
|
||||||
# TEMPLATES
|
# TEMPLATES
|
||||||
CONFIRMATION_PASSWORD_RESET_TEMPLATE = 'account/password_reset_confirm.html'
|
|
||||||
RESETTING_TOKEN_TEMPLATE = 'account/password_reset_email.html'
|
RESETTING_TOKEN_TEMPLATE = 'account/password_reset_email.html'
|
||||||
CHANGE_EMAIL_TEMPLATE = 'account/change_email.html'
|
CHANGE_EMAIL_TEMPLATE = 'account/change_email.html'
|
||||||
CONFIRM_EMAIL_TEMPLATE = 'authorization/confirm_email.html'
|
CONFIRM_EMAIL_TEMPLATE = 'authorization/confirm_email.html'
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,8 @@
|
||||||
{% blocktrans %}You're receiving this email because you want to change email address at {{ site_name }}.{% endblocktrans %}
|
{% blocktrans %}You're receiving this email because you want to change email address at {{ site_name }}.{% endblocktrans %}
|
||||||
|
|
||||||
{% trans "Please go to the following page for confirmation new email address:" %}
|
{% trans "Please go to the following page for confirmation new email address:" %}
|
||||||
{% block reset_link %}
|
|
||||||
http://{{ domain_uri }}{% url 'web:account:change-email-confirm' uidb64=uidb64 token=token %}
|
<a href="https://{{ country_code }}.{{ domain_uri }}/registered/{{ uidb64 }}/{{ token }}/">https://{{ country_code }}.{{ domain_uri }}/registered/{{ uidb64 }}/{{ token }}/</a>
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% trans "Thanks for using our site!" %}
|
{% trans "Thanks for using our site!" %}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
{% load i18n static %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{% if validlink %}
|
|
||||||
|
|
||||||
<p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
|
|
||||||
|
|
||||||
<form method="post">{% csrf_token %}
|
|
||||||
<fieldset class="module aligned">
|
|
||||||
<div class="form-row field-password1">
|
|
||||||
{{ form.new_password1.errors }}
|
|
||||||
<label for="id_new_password1">{% trans 'New password:' %}</label>
|
|
||||||
{{ form.new_password1 }}
|
|
||||||
</div>
|
|
||||||
<div class="form-row field-password2">
|
|
||||||
{{ form.new_password2.errors }}
|
|
||||||
<label for="id_new_password2">{% trans 'Confirm password:' %}</label>
|
|
||||||
{{ form.new_password2 }}
|
|
||||||
</div>
|
|
||||||
<input type="submit" value="{% trans 'Change my password' %}">
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
|
|
||||||
<p>{% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}</p>
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
|
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
|
||||||
|
|
||||||
{% trans "Please go to the following page and choose a new password:" %}
|
{% trans "Please go to the following page and choose a new password:" %}
|
||||||
|
|
||||||
<a href="https://{{ country_code }}.{{ domain_uri }}/recovery/{{ uidb64 }}/{{ token }}/">https://{{ country_code }}.{{ domain_uri }}/recovery/{{ uidb64 }}/{{ token }}/</a>
|
<a href="https://{{ country_code }}.{{ domain_uri }}/recovery/{{ uidb64 }}/{{ token }}/">https://{{ country_code }}.{{ domain_uri }}/recovery/{{ uidb64 }}/{{ token }}/</a>
|
||||||
|
|
||||||
{% trans "Thanks for using our site!" %}
|
{% trans "Thanks for using our site!" %}
|
||||||
|
|
||||||
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
|
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user