From f17bc8795638b8c40494c768c51d34d22ae9f4c7 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 11 Dec 2017 16:55:36 +0300 Subject: [PATCH] fix --- access/admin.py | 5 +- access/migrations/0001_initial.py | 12 ----- access/migrations/0002_auto_20171128_1518.py | 5 -- access/migrations/0004_auto_20171211_1418.py | 46 +++++++++++++++++ access/migrations/0004_invite_password.py | 20 -------- access/migrations/0005_auto_20171208_1820.py | 37 -------------- access/migrations/0006_auto_20171208_1849.py | 20 -------- access/models/other.py | 11 +++- access/urls.py | 1 + access/views.py | 54 +++++++++++++++----- 10 files changed, 99 insertions(+), 112 deletions(-) create mode 100644 access/migrations/0004_auto_20171211_1418.py delete mode 100644 access/migrations/0004_invite_password.py delete mode 100644 access/migrations/0005_auto_20171208_1820.py delete mode 100644 access/migrations/0006_auto_20171208_1849.py diff --git a/access/admin.py b/access/admin.py index 90fc21e..5526ca6 100755 --- a/access/admin.py +++ b/access/admin.py @@ -1,8 +1,9 @@ from django.contrib import admin -from access.models.other import Invite, Account, Progress +from access.models.other import Invite, Account, Progress, ResetPassword from access.models.user import User admin.site.register(User) admin.site.register(Account) admin.site.register(Progress) -admin.site.register(Invite) \ No newline at end of file +admin.site.register(Invite) +admin.site.register(ResetPassword) \ No newline at end of file diff --git a/access/migrations/0001_initial.py b/access/migrations/0001_initial.py index 5b51025..bb510b3 100644 --- a/access/migrations/0001_initial.py +++ b/access/migrations/0001_initial.py @@ -54,18 +54,6 @@ class Migration(migrations.Migration): 'verbose_name_plural': 'Дополнительная информация о пользователе', }, ), - migrations.CreateModel( - name='Invite', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('hash', models.CharField(max_length=15)), - ('date', models.DateTimeField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Приглошение в систему', - 'verbose_name_plural': 'Приглошения в систему', - }, - ), migrations.CreateModel( name='Progress', fields=[ diff --git a/access/migrations/0002_auto_20171128_1518.py b/access/migrations/0002_auto_20171128_1518.py index c6289c3..f130974 100644 --- a/access/migrations/0002_auto_20171128_1518.py +++ b/access/migrations/0002_auto_20171128_1518.py @@ -39,11 +39,6 @@ class Migration(migrations.Migration): name='user', field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Студент'), ), - migrations.AddField( - model_name='invite', - name='owner', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), migrations.AddField( model_name='account', name='owner', diff --git a/access/migrations/0004_auto_20171211_1418.py b/access/migrations/0004_auto_20171211_1418.py new file mode 100644 index 0000000..69cccc2 --- /dev/null +++ b/access/migrations/0004_auto_20171211_1418.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-12-11 14:18 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0003_auto_20171129_1639'), + ] + + operations = [ + migrations.CreateModel( + name='Invite', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('hash', models.CharField(max_length=15, unique=True, verbose_name='Уникальный код')), + ('date', models.DateTimeField(blank=True, null=True, verbose_name='Дата сгорания приглошения')), + ], + options={ + 'verbose_name': 'Приглошение в систему', + 'verbose_name_plural': 'Приглошения в систему', + }, + ), + migrations.CreateModel( + name='ResetPassword', + fields=[ + ('invite_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='access.Invite')), + ('password', models.CharField(max_length=63, verbose_name='Новый пароль, если есть')), + ], + options={ + 'verbose_name': 'Запрос на сброс пароля', + 'verbose_name_plural': 'Запросы на сброс пароля', + }, + bases=('access.invite',), + ), + migrations.AddField( + model_name='invite', + name='owner', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Кому приглошение'), + ), + ] diff --git a/access/migrations/0004_invite_password.py b/access/migrations/0004_invite_password.py deleted file mode 100644 index 5cf47c2..0000000 --- a/access/migrations/0004_invite_password.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2017-12-08 18:14 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('access', '0003_auto_20171129_1639'), - ] - - operations = [ - migrations.AddField( - model_name='invite', - name='password', - field=models.CharField(blank=True, max_length=63, null=True), - ), - ] diff --git a/access/migrations/0005_auto_20171208_1820.py b/access/migrations/0005_auto_20171208_1820.py deleted file mode 100644 index 2960a9b..0000000 --- a/access/migrations/0005_auto_20171208_1820.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2017-12-08 18:20 -from __future__ import unicode_literals - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('access', '0004_invite_password'), - ] - - operations = [ - migrations.AlterField( - model_name='invite', - name='date', - field=models.DateTimeField(blank=True, null=True, verbose_name='Дата сгорания приглошения'), - ), - migrations.AlterField( - model_name='invite', - name='hash', - field=models.CharField(max_length=15, verbose_name='Уникальный код'), - ), - migrations.AlterField( - model_name='invite', - name='owner', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Кому приглошение'), - ), - migrations.AlterField( - model_name='invite', - name='password', - field=models.CharField(blank=True, max_length=63, null=True, verbose_name='Новый пароль, если есть'), - ), - ] diff --git a/access/migrations/0006_auto_20171208_1849.py b/access/migrations/0006_auto_20171208_1849.py deleted file mode 100644 index cf87187..0000000 --- a/access/migrations/0006_auto_20171208_1849.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2017-12-08 18:49 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('access', '0005_auto_20171208_1820'), - ] - - operations = [ - migrations.AlterField( - model_name='invite', - name='hash', - field=models.CharField(max_length=15, unique=True, verbose_name='Уникальный код'), - ), - ] diff --git a/access/models/other.py b/access/models/other.py index 87ef350..33c44cb 100644 --- a/access/models/other.py +++ b/access/models/other.py @@ -7,16 +7,23 @@ from django.db.models import Q class Invite(models.Model): - owner = models.OneToOneField(to=settings.AUTH_USER_MODEL, verbose_name="Кому приглошение", null=True) + owner = models.OneToOneField(to=settings.AUTH_USER_MODEL, verbose_name="Кому приглошение", null=True, unique=True) hash = models.CharField(verbose_name="Уникальный код", max_length=15, unique=True) date = models.DateTimeField(verbose_name="Дата сгорания приглошения", null=True, blank=True) - password = models.CharField(max_length=63, verbose_name="Новый пароль, если есть", blank=True, null=True) class Meta: verbose_name = 'Приглошение в систему' verbose_name_plural = 'Приглошения в систему' +class ResetPassword(Invite): + password = models.CharField(max_length=63, verbose_name="Новый пароль, если есть") + + class Meta: + verbose_name = "Запрос на сброс пароля" + verbose_name_plural = "Запросы на сброс пароля" + + class Account(models.Model): GENDER_CHOICES = ( (0, 'undefined'), diff --git a/access/urls.py b/access/urls.py index ab8f2b6..d2600ec 100644 --- a/access/urls.py +++ b/access/urls.py @@ -11,5 +11,6 @@ urlpatterns = [ url(r'change_password/$', views.ChangePasswordView.as_view()), url(r'login/$', views.LoginView.as_view()), url(r'logout/$', views.LogoutView.as_view()), + url(r'reset/$', views.ResetPasswordView.as_view()), url(r'progress/$', views.UpdateProgress.as_view()), ] \ No newline at end of file diff --git a/access/views.py b/access/views.py index 6926379..22a469d 100644 --- a/access/views.py +++ b/access/views.py @@ -1,5 +1,6 @@ import random import string +import datetime from django.contrib.auth import get_user_model from django.contrib import auth @@ -11,7 +12,7 @@ from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from django.db.models import Q -from access.models.other import Invite, Progress +from access.models.other import Invite, Progress, ResetPassword from access.serializers import UserSelfSerializer, UserSearchSerializer from courses.models import Vertex @@ -27,6 +28,25 @@ class TeacherListView(APIView): class ResetPasswordView(APIView): renderer_classes = (JSONRenderer,) + @staticmethod + def get(request): + hash_key = request.GET.get('hash', None) + if not hash_key: + return Response("Нужно указать ключ", status=400) + + try: + invite = ResetPassword.objects.get(hash=hash_key) + except ResetPassword.DoesNotExist: + return Response("Приглошение не найдено", status=404) + + if invite.date > datetime.datetime.now(): + return Response("Приглошение сгорело", status=403) + + auth.login(request, invite.owner) + invite.owner.set_password(invite.password) + invite.delete() + return redirect('/') + @staticmethod def post(request): if not request.user.is_authenticated(): @@ -36,20 +56,26 @@ class ResetPasswordView(APIView): try: user = get_user_model().objects.get(email=email) try: - invite = Invite.objects.get(password__isnull=False, owner=user) - except Invite.DoesNotExist: - invite = Invite.objects.create( - owner=user, - hash=''.join(random.choice(string.ascii_letters) for _x in range(15)), - password=''.join(random.choice(string.ascii_letters) for _x in range(8)), - ) + invite = ResetPassword.objects.get(owner=user) + if invite.date > datetime.datetime.now(): + return Response("Old invite has effect", status=403) + invite.delete() + except ResetPassword.DoesNotExist: + pass + + invite = ResetPassword.objects.create( + owner=user, + hash=''.join(random.choice(string.ascii_letters) for _x in range(15)), + password=''.join(random.choice(string.ascii_letters) for _x in range(8)), + date=datetime.datetime.now() + datetime.timedelta(minutes=5) + ) send_mail( subject="Сброс пароля", message=''' Ваш новый пароль %s, (в последствии вы сможите сменить его в личном кабинете), если вы не отправляли заявку на сброс пароля просто проигнорируйте это сообщение, - для подтверждения регистрации смены пароля перейдите по ссылке - https://go.skillbox.ru/api/v1/users/reset/?hash=%s''' % (invite.password, invite.hash), + для подтверждения смены пароля перейдите по https://go.skillbox.ru/api/v1/users/reset/?hash=%s + (ссылке ссылка действительна в течении 5 минут)''' % (invite.password, invite.hash), from_email='robo@skillbox.ru', recipient_list=[user.email], ) @@ -91,7 +117,6 @@ class FindUserView(APIView): class DetailUserView(APIView): renderer_classes = (JSONRenderer,) - permission_classes = (IsAuthenticated,) @staticmethod def get(request, pk=None): @@ -124,7 +149,7 @@ class RegistrationView(APIView): @staticmethod def get(request): try: - invite = Invite.objects.get(hash=request.GET['hash'], password__isnull=True) + invite = Invite.objects.get(hash=request.GET['hash']) invite.owner.is_active = True invite.owner.save() auth.login(request, invite.owner) @@ -172,6 +197,7 @@ class LoginView(APIView): def post(request): password = request.JSON.get('password') email = request.JSON.get('email').lower() + user = None if not request.user.is_authenticated(): if not password == "skillbox": user = auth.authenticate(email=email, password=request.JSON.get('password')) @@ -186,8 +212,8 @@ class LoginView(APIView): except AttributeError: return Response("Неверный пароль", status=404) - serialized_user = UserSelfSerializer(user).data - serialized_user['is_i'] = True + serialized_user = UserSelfSerializer(user).data + serialized_user['is_i'] = True return Response(serialized_user, status=200)