From 1b19a007aea8334eaf9e3eb0f4140e06de9ccb52 Mon Sep 17 00:00:00 2001 From: Dmitry Afanasyev Date: Wed, 27 Oct 2021 20:03:41 +0300 Subject: [PATCH] add mail registration to app --- django_accounts_app/README.md | 11 +++++ django_accounts_app/admin.py | 2 +- .../migrations/create_accounts_models.py | 5 +-- django_accounts_app/models.py | 45 +++++++++++++++++++ django_accounts_app/tasks.py | 5 +-- .../templates/registration/info.html | 29 ++++++++++++ .../templates/registration/login.html | 6 ++- .../templates/registration/message.html | 4 +- .../templates/users/register.html | 14 ++++-- django_accounts_app/urls.py | 7 +-- django_accounts_app/utils.py | 27 +++++++++++ django_accounts_app/views.py | 45 ++++++++++++------- 12 files changed, 167 insertions(+), 33 deletions(-) create mode 100644 django_accounts_app/templates/registration/info.html create mode 100644 django_accounts_app/utils.py diff --git a/django_accounts_app/README.md b/django_accounts_app/README.md index a0b75d3..b87e139 100644 --- a/django_accounts_app/README.md +++ b/django_accounts_app/README.md @@ -51,8 +51,15 @@ EMAIL_HOST_PASSWORD= EMAIL_PORT= EMAIL_USE_SSL= EMAIL_USE_TLS= + +# ===== Google Recapthca ===== + +GOOGLE_RECAPTCHA_SECRET_KEY= +GOOGLE_RECAPTCHA_SECRET_SITE_KEY= ``` + + ## To urls.py from server.apps.accounts import urls as accounts_urls @@ -63,3 +70,7 @@ url_patterns path('admin/login/', login_required(lambda request: redirect('accounts/login/', permanent=True), redirect_field_name='admin/login/?next=')), + +## Add Google reCaptcha + +https://evileg.com/uk/post/283/ \ No newline at end of file diff --git a/django_accounts_app/admin.py b/django_accounts_app/admin.py index 9565e23..564cd55 100644 --- a/django_accounts_app/admin.py +++ b/django_accounts_app/admin.py @@ -9,7 +9,7 @@ class CustomUserAdmin(UserAdmin): model = CustomUser list_display = ('username', 'email', 'first_name', 'last_name', 'mobile', 'verification_code', - 'is_superuser', 'is_staff', 'is_active', ) + 'is_superuser', 'is_staff', 'is_active', 'activation_token', ) list_filter = ('username', 'first_name', 'last_name', 'user_created', 'is_superuser', 'is_staff', 'is_active', ) diff --git a/django_accounts_app/migrations/create_accounts_models.py b/django_accounts_app/migrations/create_accounts_models.py index 3ff2efb..f89039c 100644 --- a/django_accounts_app/migrations/create_accounts_models.py +++ b/django_accounts_app/migrations/create_accounts_models.py @@ -1,9 +1,7 @@ -# Generated by Django 3.2.8 on 2021-10-24 11:03 +# Generated by Django 3.2.8 on 2021-10-27 15:03 -import django.contrib.auth.models import django.contrib.auth.validators from django.db import migrations, models -import django.db.models.deletion import django.utils.timezone @@ -32,6 +30,7 @@ class Migration(migrations.Migration): ('mobile', models.CharField(blank=True, help_text='Users mobile phone', max_length=15, null=True, unique=True)), ('verification_code', models.CharField(blank=True, help_text='Verification code for bot account', max_length=10, null=True, unique=True)), ('user_created', models.DateField(auto_now_add=True, help_text='Date when user has been created', verbose_name='User created')), + ('activation_token', models.CharField(blank=True, help_text='Activation token for any user', max_length=20, null=True)), ('email', models.EmailField(help_text='User email', max_length=30, null=True, unique=True)), ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), diff --git a/django_accounts_app/models.py b/django_accounts_app/models.py index e303885..0c01452 100644 --- a/django_accounts_app/models.py +++ b/django_accounts_app/models.py @@ -11,6 +11,8 @@ class CustomUser(AbstractUser): help_text='Verification code for bot account') user_created = models.DateField(editable=False, auto_now_add=True, verbose_name='User created', help_text='Date when user has been created') + activation_token = models.CharField(max_length=20, null=True, blank=True, + help_text='Activation token for any user') email = models.EmailField(max_length=30, unique=True, blank=False, null=True, help_text='User email') USERNAME_FIELD = 'username' REQUIRED_FIELDS = ['email'] @@ -18,3 +20,46 @@ class CustomUser(AbstractUser): def __str__(self): return self.username + + +# # ----- ToDO: Enable this to email verification -------- +# +# from django.dispatch import receiver +# from django.conf import settings +# from .tasks import mail_send +# from server.settings.components.logging import main_logger +# +# +# def user_tokens() -> dict: +# +# tokens_dict = dict() +# +# def generate_token(token_length: int) -> str: +# from random import choice +# token_chars = 'QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890' +# generated_token = '' +# for i in range(token_length): +# generated_token += choice(token_chars) +# return generated_token +# +# tokens_dict['activation_token'] = generate_token(token_length=20) +# +# return tokens_dict +# +# +# @receiver(models.signals.post_save, sender=CustomUser) +# def post_save_user_signal_handler(sender, instance, created, **kwargs): +# +# if created and instance.username != 'admin': +# instance.activation_token = user_tokens()['activation_token'] +# instance.save() +# try: +# user = CustomUser.objects.get(username=instance.username) +# email = instance.email +# subject = 'Welcome to book bot administration' +# username = f'{instance.first_name} {instance.last_name}' +# text = f'https://{settings.DOMAIN_NAME}/accounts/complete_registration/{user.activation_token}' +# +# mail_send(to_email=email, subject=subject, username=username, text_content=text) +# except Exception as e: +# main_logger.error(f'Email not send to user {instance.username}. Reason: {e}') diff --git a/django_accounts_app/tasks.py b/django_accounts_app/tasks.py index 15b57af..6154f69 100644 --- a/django_accounts_app/tasks.py +++ b/django_accounts_app/tasks.py @@ -10,7 +10,6 @@ def mail_send(to_email: str, subject: str, text_content: str = '', **kwargs) -> username = kwargs.get('username') from_email = settings.DEFAULT_FROM_EMAIL - msg_html = render_to_string('registration/message.html', - {'username': username}) + msg_html = render_to_string('registration/message.html', {'username': username, 'text_content': text_content}) - send_mail(subject, text_content, from_email, [to_email], html_message=msg_html, fail_silently=False) + send_mail(subject, '', from_email, [to_email], html_message=msg_html, fail_silently=False) diff --git a/django_accounts_app/templates/registration/info.html b/django_accounts_app/templates/registration/info.html new file mode 100644 index 0000000..8ad6c80 --- /dev/null +++ b/django_accounts_app/templates/registration/info.html @@ -0,0 +1,29 @@ +{% extends "base.html" %} + +{% block content %} + + + + +Bootstrap Simple Login Form with Blue Background + + + + + + +
+

{{message}}

+
+ +{% endblock %} diff --git a/django_accounts_app/templates/registration/login.html b/django_accounts_app/templates/registration/login.html index 30152ca..a8203ca 100644 --- a/django_accounts_app/templates/registration/login.html +++ b/django_accounts_app/templates/registration/login.html @@ -98,7 +98,11 @@ body {
{% csrf_token %}
- + {% if form.username.value %} + + {% else %} + + {% endif %}
diff --git a/django_accounts_app/templates/registration/message.html b/django_accounts_app/templates/registration/message.html index 14e0b6a..08568df 100644 --- a/django_accounts_app/templates/registration/message.html +++ b/django_accounts_app/templates/registration/message.html @@ -1,6 +1,8 @@
-

Добро пожаловать, {{ username }}!

+

Добро пожаловать, {{username}}!

+

Чтобы активировать учётную запись перейдите по ссылке: + Завершить регистрацию

diff --git a/django_accounts_app/templates/users/register.html b/django_accounts_app/templates/users/register.html index 1708fdd..ee2704b 100644 --- a/django_accounts_app/templates/users/register.html +++ b/django_accounts_app/templates/users/register.html @@ -93,7 +93,6 @@ body {
@@ -144,9 +142,17 @@ body {
{{ form.password2.errors }}
+ +
+ {% if messages %} + {% for message in messages %} + {{ message }} + {% endfor %} + {% endif %} +
-
+
{{ form.non_field_errors }}
Already have an account? Login here
diff --git a/django_accounts_app/urls.py b/django_accounts_app/urls.py index 69476aa..6babcee 100644 --- a/django_accounts_app/urls.py +++ b/django_accounts_app/urls.py @@ -1,11 +1,12 @@ - from django.urls import path, include -from .views import dashboard, RegisterUser +from .views import dashboard, RegisterUser, success_registration, not_registered +from server.apps.accounts.utils import check_recaptcha app_name = 'accounts' urlpatterns = [ path('', include("django.contrib.auth.urls")), path('dashboard/', dashboard, name='dashboard'), - path('registration/', RegisterUser.as_view(), name='registration') + path('registration/', check_recaptcha(RegisterUser.as_view()), name='registration'), + path('complete_registration/', success_registration, name='success_registration'), ] diff --git a/django_accounts_app/utils.py b/django_accounts_app/utils.py new file mode 100644 index 0000000..e277179 --- /dev/null +++ b/django_accounts_app/utils.py @@ -0,0 +1,27 @@ +from django.conf import settings +from django.contrib import messages +from functools import wraps + +import requests + + +def check_recaptcha(function): + @wraps(function) + def new_func(request, *args, **kwargs): + request.recaptcha_is_valid = None + if request.method == 'POST': + recaptcha_response = request.POST.get('g-recaptcha-response') + data = { + 'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY, + 'response': recaptcha_response + } + r = requests.post('https://www.google.com/recaptcha/api/siteverify', data=data) + result = r.json() + if result['success']: + request.recaptcha_is_valid = True + else: + request.recaptcha_is_valid = False + messages.error(request, 'Invalid reCAPTCHA. Please try again.') + return function(request, *args, **kwargs) + + return new_func diff --git a/django_accounts_app/views.py b/django_accounts_app/views.py index 2e7ce94..d72e026 100644 --- a/django_accounts_app/views.py +++ b/django_accounts_app/views.py @@ -2,33 +2,44 @@ from django.forms import BaseModelForm from django.shortcuts import render, redirect from django.contrib.auth import login from django.views.generic import CreateView -from django.http import HttpResponse +from django.http import HttpResponse, HttpRequest from server.apps.accounts.forms import CustomUserCreationForm +from server.apps.accounts.models import CustomUser from django.core.validators import validate_email -from .tasks import mail_send +from django.core.exceptions import ObjectDoesNotExist # Create your views here. -def dashboard(request): +def dashboard(request: HttpRequest) -> HttpResponse: return render(request, "users/dashboard.html", {}) +def not_registered(request: HttpRequest) -> HttpResponse: + return redirect('accounts:login') + + +def success_registration(request: HttpRequest, activation_token: str) -> HttpResponse: + try: + user = CustomUser.objects.get(activation_token=activation_token) + user.backend = 'django.contrib.auth.backends.ModelBackend' + user.is_staff = True + user.activation_token = '' + user.save() + login(request, user) + return redirect('admin:index') + except ObjectDoesNotExist: + message = 'Activation token not found' + return render(request, 'registration/info.html', {'message': message}, status=404) + + class RegisterUser(CreateView): form_class = CustomUserCreationForm template_name = 'users/register.html' def form_valid(self, form: BaseModelForm) -> HttpResponse: - user = form.save() - user.backend = 'django.contrib.auth.backends.ModelBackend' - - validate_email(form.instance.email) - email = form.instance.email - username = f'{form.instance.first_name} {form.instance.last_name}' - subject = 'Welcome to book bot administration' - mail_send(to_email=email, subject=subject, username=username) - - login(self.request, user) - - return redirect('admin:index') - - + if self.request.recaptcha_is_valid: + form.save() + validate_email(form.instance.email) + message = 'Please check your email for continue registration' + return render(self.request, 'registration/info.html', {'message': message}) + return render(self.request, 'users/register.html', self.get_context_data())