version 0.0.5.7: added method to reset password (until without confirmation)
This commit is contained in:
parent
4fbee551c3
commit
cfc6e05595
|
|
@ -45,3 +45,12 @@ 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', )
|
||||||
|
|
|
||||||
32
apps/account/migrations/0003_resetpasswordtoken.py
Normal file
32
apps/account/migrations/0003_resetpasswordtoken.py
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-08-11 16:25
|
||||||
|
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('account', '0002_user_newsletter'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ResetPasswordToken',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')),
|
||||||
|
('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||||
|
('source', models.PositiveSmallIntegerField(choices=[(0, 'Mobile'), (1, 'Web')], default=0, verbose_name='Source')),
|
||||||
|
('key', models.CharField(max_length=255, verbose_name='Key')),
|
||||||
|
('ip_address', models.GenericIPAddressField(blank=True, default='', null=True, verbose_name='The IP address of this session')),
|
||||||
|
('expiry_datetime', models.DateTimeField(blank=True, null=True, verbose_name='Expiration datetime')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='password_reset_tokens', to=settings.AUTH_USER_MODEL, verbose_name='The User which is associated to this password reset token')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Password Reset Token',
|
||||||
|
'verbose_name_plural': 'Password Reset Tokens',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,9 +1,17 @@
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import AbstractUser, UserManager as BaseUserManager
|
from django.contrib.auth.models import AbstractUser, UserManager as BaseUserManager
|
||||||
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
|
from django.core.mail import send_mail
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
|
|
||||||
from utils.models import ImageMixin
|
from authorization.models import Application
|
||||||
|
from utils.models import ImageMixin, ProjectBaseMixin, PlatformMixin
|
||||||
|
|
||||||
|
|
||||||
class UserManager(BaseUserManager):
|
class UserManager(BaseUserManager):
|
||||||
|
|
@ -53,17 +61,133 @@ class User(ImageMixin, AbstractUser):
|
||||||
"""String method."""
|
"""String method."""
|
||||||
return "%s:%s" % (self.email, self.get_short_name())
|
return "%s:%s" % (self.email, self.get_short_name())
|
||||||
|
|
||||||
|
def change_status(self, switcher: bool = False):
|
||||||
|
"""Method to set user status to active or inactive"""
|
||||||
|
self.is_active = switcher
|
||||||
|
self.save()
|
||||||
|
|
||||||
def remove_token(self):
|
def remove_token(self):
|
||||||
Token.objects.filter(user=self).delete()
|
Token.objects.filter(user=self).delete()
|
||||||
|
|
||||||
def remove_access_tokens(self, source):
|
def remove_access_tokens(self, source: Union[int, Union[tuple, list]]):
|
||||||
"""Method to remove user access tokens"""
|
"""Method to remove user access tokens"""
|
||||||
self.oauth2_provider_accesstoken.filter(application__source=source)\
|
source = source if isinstance(source, list) else [source, ]
|
||||||
|
self.oauth2_provider_accesstoken.filter(application__source__in=source)\
|
||||||
.delete()
|
.delete()
|
||||||
|
|
||||||
def revoke_refresh_tokens(self, source):
|
def revoke_refresh_tokens(self, source: Union[int, tuple, list]):
|
||||||
"""Method to remove user refresh tokens"""
|
"""Method to remove user refresh tokens"""
|
||||||
|
source = source if isinstance(source, list) else [source, ]
|
||||||
refresh_tokens = self.oauth2_provider_refreshtoken.filter(
|
refresh_tokens = self.oauth2_provider_refreshtoken.filter(
|
||||||
application__source=source)
|
application__source__in=source)
|
||||||
for token in refresh_tokens:
|
if refresh_tokens.exists():
|
||||||
token.revoke()
|
for token in refresh_tokens:
|
||||||
|
token.revoke()
|
||||||
|
|
||||||
|
def get_body_email_message(self, subject: str, message: str):
|
||||||
|
"""Prepare the body of the email message"""
|
||||||
|
return {
|
||||||
|
'subject': subject,
|
||||||
|
'message': str(message),
|
||||||
|
'from_email': settings.EMAIL_HOST_USER,
|
||||||
|
'recipient_list': [self.email, ]
|
||||||
|
}
|
||||||
|
|
||||||
|
def send_email(self, subject: str, message: str):
|
||||||
|
"""Send an email to reset user password"""
|
||||||
|
# todo: move to celery as celery task
|
||||||
|
send_mail(**self.get_body_email_message(subject=subject,
|
||||||
|
message=message))
|
||||||
|
|
||||||
|
|
||||||
|
class ResetPasswordTokenQuerySet(models.QuerySet):
|
||||||
|
"""Reset password token query set"""
|
||||||
|
|
||||||
|
def expired(self):
|
||||||
|
"""Show only expired"""
|
||||||
|
return self.filter(expiry_datetime__gt=timezone.now())
|
||||||
|
|
||||||
|
def valid(self):
|
||||||
|
"""Show only valid"""
|
||||||
|
return self.filter(expiry_datetime__lt=timezone.now())
|
||||||
|
|
||||||
|
def by_user(self, user):
|
||||||
|
"""Show obj by user"""
|
||||||
|
return self.filter(user=user)
|
||||||
|
|
||||||
|
|
||||||
|
class ResetPasswordToken(PlatformMixin, ProjectBaseMixin):
|
||||||
|
"""Reset password model"""
|
||||||
|
|
||||||
|
RESETTING_TOKEN_TEMPLATE_NAME = 'account/password_reset_email.html'
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
def generate_key(self):
|
||||||
|
"""generates a pseudo random code"""
|
||||||
|
return default_token_generator.make_token(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_key()
|
||||||
|
return super(ResetPasswordToken, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_reset_password_template(self):
|
||||||
|
"""Get reset password template"""
|
||||||
|
return render_to_string(
|
||||||
|
template_name=self.RESETTING_TOKEN_TEMPLATE_NAME,
|
||||||
|
context={'token': self.key})
|
||||||
|
|
||||||
|
def send_reset_password_request(self):
|
||||||
|
"""Method to reset user password"""
|
||||||
|
subject = _('Password resetting')
|
||||||
|
|
||||||
|
# Remove access token and revoke refresh tokens
|
||||||
|
self.user.remove_access_tokens(source=[Application.MOBILE,
|
||||||
|
Application.WEB])
|
||||||
|
# Make user temporarily unavailable
|
||||||
|
self.user.change_status(switcher=False)
|
||||||
|
|
||||||
|
# Send an email with url for resetting a password
|
||||||
|
self.user.send_email(subject=subject,
|
||||||
|
message=self.get_reset_password_template())
|
||||||
|
|
|
||||||
60
apps/account/serializers/common.py
Normal file
60
apps/account/serializers/common.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
"""Common serializers"""
|
||||||
|
from fcm_django.models import FCMDevice
|
||||||
|
from rest_framework import serializers, exceptions
|
||||||
|
|
||||||
|
from account import models
|
||||||
|
|
||||||
|
|
||||||
|
# User serializers
|
||||||
|
class UserSerializer(serializers.ModelSerializer):
|
||||||
|
"""User serializer."""
|
||||||
|
class Meta:
|
||||||
|
model = models.User
|
||||||
|
fields = [
|
||||||
|
'first_name',
|
||||||
|
'last_name',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Firebase Cloud Messaging serializers
|
||||||
|
class FCMDeviceSerializer(serializers.ModelSerializer):
|
||||||
|
"""FCM Device model serializer"""
|
||||||
|
class Meta:
|
||||||
|
model = FCMDevice
|
||||||
|
fields = ('id', 'name', 'registration_id', 'device_id',
|
||||||
|
'active', 'date_created', 'type')
|
||||||
|
read_only_fields = ('id', 'date_created',)
|
||||||
|
extra_kwargs = {'active': {'default': True}}
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
regid = attrs.get('registration_id')
|
||||||
|
dtype = attrs.get('type')
|
||||||
|
if regid and dtype and self.Meta.model.objects.filter(
|
||||||
|
registration_id=regid).exclude(type=dtype).count():
|
||||||
|
raise exceptions.ValidationError(
|
||||||
|
{'registration_id': 'This field must be unique.'})
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(FCMDeviceSerializer, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['type'].help_text = (
|
||||||
|
'Should be one of ["%s"]' %
|
||||||
|
'", "'.join([i for i in self.fields['type'].choices]))
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
user = self.context['request'].user
|
||||||
|
if not user.is_anonymous:
|
||||||
|
validated_data['user'] = user
|
||||||
|
device = FCMDevice.objects.create(**validated_data)
|
||||||
|
return device
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
|
||||||
|
user = self.context['request'].user
|
||||||
|
if not user.is_anonymous:
|
||||||
|
instance.user = user
|
||||||
|
instance.save()
|
||||||
|
else:
|
||||||
|
instance.user = None
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|
@ -1,57 +1,7 @@
|
||||||
from fcm_django.models import FCMDevice
|
from rest_framework import serializers
|
||||||
from rest_framework import serializers, exceptions
|
|
||||||
|
|
||||||
from account import models
|
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(serializers.ModelSerializer):
|
class PasswordResetSerializer(serializers.Serializer):
|
||||||
"""User serializer."""
|
"""Serializer for reset password"""
|
||||||
class Meta:
|
|
||||||
model = models.User
|
|
||||||
fields = [
|
|
||||||
'first_name',
|
|
||||||
'last_name',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
password = serializers.CharField()
|
||||||
class FCMDeviceSerializer(serializers.ModelSerializer):
|
|
||||||
"""FCM Device model serializer"""
|
|
||||||
class Meta:
|
|
||||||
model = FCMDevice
|
|
||||||
fields = ('id', 'name', 'registration_id', 'device_id',
|
|
||||||
'active', 'date_created', 'type')
|
|
||||||
read_only_fields = ('id', 'date_created',)
|
|
||||||
extra_kwargs = {'active': {'default': True}}
|
|
||||||
|
|
||||||
def validate(self, attrs):
|
|
||||||
regid = attrs.get('registration_id')
|
|
||||||
dtype = attrs.get('type')
|
|
||||||
if regid and dtype and self.Meta.model.objects.filter(
|
|
||||||
registration_id=regid).exclude(type=dtype).count():
|
|
||||||
raise exceptions.ValidationError(
|
|
||||||
{'registration_id': 'This field must be unique.'})
|
|
||||||
return attrs
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(FCMDeviceSerializer, self).__init__(*args, **kwargs)
|
|
||||||
self.fields['type'].help_text = (
|
|
||||||
'Should be one of ["%s"]' %
|
|
||||||
'", "'.join([i for i in self.fields['type'].choices]))
|
|
||||||
|
|
||||||
def create(self, validated_data):
|
|
||||||
user = self.context['request'].user
|
|
||||||
if not user.is_anonymous:
|
|
||||||
validated_data['user'] = user
|
|
||||||
device = FCMDevice.objects.create(**validated_data)
|
|
||||||
return device
|
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
|
||||||
|
|
||||||
user = self.context['request'].user
|
|
||||||
if not user.is_anonymous:
|
|
||||||
instance.user = user
|
|
||||||
instance.save()
|
|
||||||
else:
|
|
||||||
instance.user = None
|
|
||||||
instance.save()
|
|
||||||
return instance
|
|
||||||
|
|
|
||||||
11
apps/account/urls/common.py
Normal file
11
apps/account/urls/common.py
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
"""Common account URLs"""
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from account.views import common as views
|
||||||
|
|
||||||
|
app_name = 'account'
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('user/', views.UserView.as_view(), name='user_get_update'),
|
||||||
|
path('device/', views.FCMDeviceViewSet.as_view(), name='fcm_device_create'),
|
||||||
|
]
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
"""Account app urlconf."""
|
"""Web account URLs"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
|
from account.urls import common as common_views
|
||||||
from account.views import web as views
|
from account.views import web as views
|
||||||
|
|
||||||
app_name = 'account'
|
app_name = 'account'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns_api = [
|
||||||
path('user/', views.UserView.as_view(), name='user_get_update'),
|
path('reset-password/', views.PasswordResetView.as_view(),
|
||||||
path('device/', views.FCMDeviceViewSet.as_view(), name='fcm_device_create'),
|
name='password-reset'),
|
||||||
# path('reset-password/', views.ResetPasswordView.as_view(), name='reset-password'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
urlpatterns = urlpatterns_api + \
|
||||||
|
common_views.urlpatterns
|
||||||
|
|
|
||||||
55
apps/account/views/common.py
Normal file
55
apps/account/views/common.py
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
"""Common account views"""
|
||||||
|
from fcm_django.models import FCMDevice
|
||||||
|
from rest_framework import generics, status
|
||||||
|
from rest_framework import permissions
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from account import models
|
||||||
|
from account.serializers import common as serializers
|
||||||
|
|
||||||
|
|
||||||
|
# User views
|
||||||
|
class UserView(generics.RetrieveUpdateAPIView):
|
||||||
|
"""### User update view."""
|
||||||
|
serializer_class = serializers.UserSerializer
|
||||||
|
queryset = models.User.objects.active()
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
return self.request.user
|
||||||
|
|
||||||
|
|
||||||
|
# Firebase Cloud Messaging
|
||||||
|
class FCMDeviceViewSet(generics.GenericAPIView):
|
||||||
|
"""FCMDevice registration view.
|
||||||
|
|
||||||
|
* Pair of fields **registration_id** and **type** should be unique.
|
||||||
|
* In case of requested device existance, existing device will be returned
|
||||||
|
instead of creating new one.
|
||||||
|
"""
|
||||||
|
|
||||||
|
serializer_class = serializers.FCMDeviceSerializer
|
||||||
|
lookup_fields = ('registration_id', 'type',)
|
||||||
|
queryset = FCMDevice.objects.all()
|
||||||
|
permission_classes = (permissions.AllowAny,)
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
"""Override post method."""
|
||||||
|
instance = self.get_object_or_none()
|
||||||
|
serializer = self.get_serializer(instance, data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
serializer.save()
|
||||||
|
return Response(
|
||||||
|
serializer.data, status=status.HTTP_200_OK if instance else status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
def get_object_or_none(self):
|
||||||
|
"""Object as resylt and the view is displaying or None."""
|
||||||
|
queryset = self.get_queryset() # get the base queryset
|
||||||
|
queryset = self.filter_queryset(queryset) # apply any filter backends
|
||||||
|
# generate filter
|
||||||
|
filter = {f: self.request.data.get(f) for f in self.lookup_fields
|
||||||
|
if self.request.data.get(f)}
|
||||||
|
|
||||||
|
# get object and check permissions or return None
|
||||||
|
obj = queryset.filter(**filter).first()
|
||||||
|
obj and self.check_object_permissions(self.request, obj)
|
||||||
|
return obj
|
||||||
|
|
@ -1,52 +1,31 @@
|
||||||
from fcm_django.models import FCMDevice
|
"""Web account views"""
|
||||||
from rest_framework import generics, status
|
|
||||||
from rest_framework import permissions
|
from rest_framework import permissions
|
||||||
|
from rest_framework import status, generics
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from account import models
|
from account import models
|
||||||
from account.serializers import web as serializers
|
from account.serializers import web as serializers
|
||||||
|
from utils import exceptions as utils_exceptions
|
||||||
|
|
||||||
|
|
||||||
class UserView(generics.RetrieveUpdateAPIView):
|
# Password reset
|
||||||
"""### User update view."""
|
class PasswordResetView(generics.GenericAPIView):
|
||||||
serializer_class = serializers.UserSerializer
|
"""View for resetting user password"""
|
||||||
queryset = models.User.objects.active()
|
|
||||||
|
|
||||||
def get_object(self):
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
return self.request.user
|
serializer_class = serializers.PasswordResetSerializer
|
||||||
|
|
||||||
|
|
||||||
class FCMDeviceViewSet(generics.GenericAPIView):
|
|
||||||
"""FCMDevice registration view.
|
|
||||||
|
|
||||||
* Pair of fields **registration_id** and **type** should be unique.
|
|
||||||
* In case of requested device existance, existing device will be returned
|
|
||||||
instead of creating new one.
|
|
||||||
"""
|
|
||||||
|
|
||||||
serializer_class = serializers.FCMDeviceSerializer
|
|
||||||
lookup_fields = ('registration_id', 'type',)
|
|
||||||
queryset = FCMDevice.objects.all()
|
|
||||||
permission_classes = (permissions.AllowAny, )
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
"""Override post method."""
|
"""Post-method for password resetting"""
|
||||||
instance = self.get_object_or_none()
|
user = request.user
|
||||||
serializer = self.get_serializer(instance, data=request.data)
|
obj = models.ResetPasswordToken.objects.create(
|
||||||
serializer.is_valid(raise_exception=True)
|
user=user,
|
||||||
serializer.save()
|
ip_address=request.META.get('REMOTE_ADDR'),
|
||||||
return Response(
|
source=models.ResetPasswordToken.MOBILE
|
||||||
serializer.data, status=status.HTTP_200_OK if instance else status.HTTP_201_CREATED)
|
)
|
||||||
|
try:
|
||||||
def get_object_or_none(self):
|
# todo: make as celery task
|
||||||
"""Object as resylt and the view is displaying or None."""
|
obj.send_reset_password_request()
|
||||||
queryset = self.get_queryset() # get the base queryset
|
return Response(status=status.HTTP_200_OK)
|
||||||
queryset = self.filter_queryset(queryset) # apply any filter backends
|
except:
|
||||||
# generate filter
|
raise utils_exceptions.EmailSendingError(user.email)
|
||||||
filter = {f: self.request.data.get(f) for f in self.lookup_fields
|
|
||||||
if self.request.data.get(f)}
|
|
||||||
|
|
||||||
# get object and check permissions or return None
|
|
||||||
obj = queryset.filter(**filter).first()
|
|
||||||
obj and self.check_object_permissions(self.request, obj)
|
|
||||||
return obj
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from oauth2_provider.models import AbstractApplication
|
|
||||||
from oauth2_provider import models as oauth2_models
|
from oauth2_provider import models as oauth2_models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from oauth2_provider.models import AbstractApplication
|
||||||
|
|
||||||
|
from utils.models import PlatformMixin
|
||||||
|
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
@ -19,17 +20,9 @@ class ApplicationManager(oauth2_models.ApplicationManager):
|
||||||
"""Application manager"""
|
"""Application manager"""
|
||||||
|
|
||||||
|
|
||||||
class Application(AbstractApplication):
|
class Application(PlatformMixin, AbstractApplication):
|
||||||
"""Custom oauth2 application model"""
|
"""Custom oauth2 application model"""
|
||||||
MOBILE = 0
|
|
||||||
WEB = 1
|
|
||||||
|
|
||||||
SOURCES = (
|
|
||||||
(MOBILE, _('Mobile')),
|
|
||||||
(WEB, _('Web')),
|
|
||||||
)
|
|
||||||
source = models.PositiveSmallIntegerField(choices=SOURCES, default=MOBILE,
|
|
||||||
verbose_name=_('Source'))
|
|
||||||
objects = ApplicationManager.from_queryset(ApplicationQuerySet)()
|
objects = ApplicationManager.from_queryset(ApplicationQuerySet)()
|
||||||
|
|
||||||
class Meta(AbstractApplication.Meta):
|
class Meta(AbstractApplication.Meta):
|
||||||
|
|
|
||||||
|
|
@ -26,3 +26,24 @@ class UserNotFoundError(ProjectBaseException):
|
||||||
"""The exception should be thrown when the user cannot get"""
|
"""The exception should be thrown when the user cannot get"""
|
||||||
status_code = status.HTTP_404_NOT_FOUND
|
status_code = status.HTTP_404_NOT_FOUND
|
||||||
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):
|
||||||
|
"""The exception should be thrown when unable to send an email"""
|
||||||
|
status_code = status.HTTP_400_BAD_REQUEST
|
||||||
|
default_detail = _('Unable to send message to mailbox %s')
|
||||||
|
|
||||||
|
def __init__(self, recipient=None):
|
||||||
|
if recipient:
|
||||||
|
self.default_detail = {
|
||||||
|
'detail': self.default_detail % recipient,
|
||||||
|
}
|
||||||
|
super().__init__()
|
||||||
|
|
|
||||||
|
|
@ -81,3 +81,20 @@ class ImageMixin(models.Model):
|
||||||
image_tag.short_description = _('Image')
|
image_tag.short_description = _('Image')
|
||||||
image_tag.allow_tags = True
|
image_tag.allow_tags = True
|
||||||
|
|
||||||
|
|
||||||
|
class PlatformMixin(models.Model):
|
||||||
|
"""Platforms"""
|
||||||
|
|
||||||
|
MOBILE = 0
|
||||||
|
WEB = 1
|
||||||
|
|
||||||
|
SOURCES = (
|
||||||
|
(MOBILE, _('Mobile')),
|
||||||
|
(WEB, _('Web')),
|
||||||
|
)
|
||||||
|
source = models.PositiveSmallIntegerField(choices=SOURCES, default=MOBILE,
|
||||||
|
verbose_name=_('Source'))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta class"""
|
||||||
|
abstract = True
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ ROOT_URLCONF = 'project.urls'
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': [],
|
'DIRS': [os.path.join(BASE_DIR, 'templates'), ],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
|
|
@ -264,7 +264,7 @@ SMS_SENDER = 'GM'
|
||||||
EMAIL_USE_TLS = True
|
EMAIL_USE_TLS = True
|
||||||
EMAIL_HOST = 'smtp.gmail.com'
|
EMAIL_HOST = 'smtp.gmail.com'
|
||||||
EMAIL_HOST_USER = 'anatolyfeteleu@gmail.com'
|
EMAIL_HOST_USER = 'anatolyfeteleu@gmail.com'
|
||||||
EMAIL_HOST_PASSWORD = 'MA1gu4YNiT'
|
EMAIL_HOST_PASSWORD = 'nggrlnbehzksgmbt'
|
||||||
EMAIL_PORT = 587
|
EMAIL_PORT = 587
|
||||||
|
|
||||||
# Django Rest Swagger
|
# Django Rest Swagger
|
||||||
|
|
@ -319,3 +319,6 @@ THUMBNAIL_ALIASES = {
|
||||||
'gallery': {'size': (240, 160), 'crop': True},
|
'gallery': {'size': (240, 160), 'crop': True},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Password reset
|
||||||
|
RESETTING_TOKEN_EXPIRATION = 24 # hours
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,5 @@ ALLOWED_HOSTS = ['*', ]
|
||||||
|
|
||||||
SEND_SMS = False
|
SEND_SMS = False
|
||||||
SMS_CODE_SHOW = True
|
SMS_CODE_SHOW = True
|
||||||
|
|
||||||
|
DOMAIN_URI = 'localhost:8000'
|
||||||
|
|
|
||||||
13
project/templates/account/password_reset_email.html
Normal file
13
project/templates/account/password_reset_email.html
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{% load i18n %}{% autoescape off %}
|
||||||
|
{% 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:" %}
|
||||||
|
{% block reset_link %}
|
||||||
|
http://{{ settings.DOMAIN_URI }}{% url 'web:account:password-reset-confirm' token=token %}
|
||||||
|
{% endblock %}
|
||||||
|
{% trans 'Your username, in case you’ve forgotten:' %} {{ user.get_username }}
|
||||||
|
|
||||||
|
{% trans "Thanks for using our site!" %}
|
||||||
|
|
||||||
|
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
|
||||||
|
{% endautoescape %}
|
||||||
Loading…
Reference in New Issue
Block a user