add mail registration to app

This commit is contained in:
Dmitry Afanasyev 2021-10-27 20:03:41 +03:00
parent 196e9ab73b
commit 1b19a007ae
12 changed files with 167 additions and 33 deletions

View File

@ -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/

View File

@ -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', )

View File

@ -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')),

View File

@ -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}')

View File

@ -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)

View File

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% block content %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:400,700">
<title>Bootstrap Simple Login Form with Blue Background</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
<style>
body {
color: #fff;
background: #3598dc;
font-family: 'Roboto', sans-serif;
}
.info-form {
padding-top: 100px;
text-align: center;
}
</style>
<div class="info-form" >
<h3>{{message}}</h3>
</div>
{% endblock %}

View File

@ -98,7 +98,11 @@ body {
<hr>
{% csrf_token %}
<div class="form-group">
<input type="text" class="form-control" name="username" placeholder="Username" required="required">
{% if form.username.value %}
<input type="text" class="form-control" name="username" value="{{ form.username.value }}">
{% else %}
<input type="text" class="form-control" name="username" placeholder="Username" required="required">
{% endif %}
</div>
<div class="form-group">
<input type="password" class="form-control" name="password" placeholder="Password" required="required">

View File

@ -1,6 +1,8 @@
<!doctype html>
<body>
<div>
<p style="color: green">Добро пожаловать, {{ username }}!</p>
<p style="color: green">Добро пожаловать, {{username}}!</p>
<p style="color: green">Чтобы активировать учётную запись перейдите по ссылке:
<a href="{{text_content}}">Завершить регистрацию</a></p>
</div>
</body>

View File

@ -93,7 +93,6 @@ body {
</style>
<div class="signup-form">
<form method="post">
<h2>Sign Up</h2>
<p>Please fill in this form to create an account!</p>
<hr>
{% csrf_token %}
@ -131,9 +130,8 @@ body {
{% if form.mobile.value %}
<input type="text" class="form-control" name="mobile" value="{{ form.mobile.value }}">
{% else %}
<input id="mobile" type="row" class="form-control" name="mobile" placeholder="Mobile" >
<input id="mobile" type="row" class="form-control" name="mobile" placeholder="Mobile (Not required)">
{% endif %}
<label for="mobile">Not required</label>
<div class="form-group">{{ form.mobile.errors }}</div>
</div>
<div class="form-group">
@ -144,9 +142,17 @@ body {
<div class="form-group">{{ form.password2.errors }}</div>
</div>
<script src='https://www.google.com/recaptcha/api.js'></script>
<div class="form-group g-recaptcha" data-sitekey="6LcaYfgcAAAAAEX1GE9myoMEA2xVhWOrXqKUke_j"></div>
{% if messages %}
{% for message in messages %}
{{ message }}
{% endfor %}
{% endif %}
<div class="form-group">
<button type="submit" class="btn btn-primary btn-lg">Sign Up</button>
</div>
</div>
<div class="form-group">{{ form.non_field_errors }}</div>
</form>
<div class="hint-text">Already have an account? <a href="{% url 'accounts:login' %}">Login here</a></div>

View File

@ -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/<str:activation_token>', success_registration, name='success_registration'),
]

View File

@ -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

View File

@ -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())