accounts authorization app

This commit is contained in:
Dmitry Afanasyev 2021-10-25 18:48:05 +03:00
parent d518d1f8da
commit 3f14070560
19 changed files with 568 additions and 0 deletions

View File

View File

@ -0,0 +1,48 @@
from server.apps.accounts.models import CustomUser, CustomGroup
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin, GroupAdmin, Group
@admin.register(CustomUser)
class CustomUserAdmin(UserAdmin):
model = CustomUser
list_display = ('username', 'email', 'first_name', 'last_name', 'mobile', 'verification_code',
'is_superuser', 'is_staff', 'is_active', )
list_filter = ('username', 'first_name', 'last_name', 'user_created', 'is_superuser', 'is_staff', 'is_active', )
ordering = ('user_created', 'username', 'first_name', 'is_superuser', 'is_staff', 'is_active', )
fieldsets = (
(None, {'fields': ('username', 'password')}),
('Main fields', {'fields': ('first_name', 'last_name', 'verification_code', 'user_created', )}
),
('Contacts', {'fields': ('email', 'mobile', )}
),
('Permissions', {'fields': ('groups', 'user_permissions', 'is_superuser', 'is_staff', 'is_active')}
),
)
add_fieldsets = (
('Main fields', {
'classes': ('wide', ),
'fields': ('username', 'email', 'mobile', 'password1', 'password2', 'verification_code', )
}),
('Permissions', {
'classes': ('wide', ),
'fields': ('groups', 'user_permissions', 'is_superuser', 'is_staff', 'is_active')
})
)
readonly_fields = ('user_created', )
search_fields = ('username', 'first_name', 'last_name', 'mobile', 'email')
filter_horizontal = ('groups', 'user_permissions',)
admin.site.unregister(Group)
@admin.register(CustomGroup)
class CustomGroupAdmin(GroupAdmin):
...

View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class CustomUserConfig(AppConfig):
name = 'server.apps.accounts'
verbose_name = 'Admin User manager'

View File

@ -0,0 +1,18 @@
from django import forms
from django.contrib.auth.forms import UserCreationForm
from server.apps.accounts.models import CustomUser
class CustomUserCreationForm(UserCreationForm):
# username = forms.CharField(label='Логин', widget=forms.TextInput(attrs={'class': 'form-input'}))
# email = forms.EmailField(label='Email', widget=forms.EmailInput(attrs={'class': 'form-input'}))
# password1 = forms.CharField(label='Пароль', widget=forms.PasswordInput(attrs={'class': 'form-input'}))
# password2 = forms.CharField(label='Повтор пароля',
# widget=forms.PasswordInput(attrs={'class': 'form-input'}))
class Meta:
model = CustomUser
fields = ('username', 'first_name', 'last_name', 'email', 'mobile', 'password1', 'password2')

View File

@ -0,0 +1,27 @@
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import ugettext_lazy as _
class CustomUserManager(BaseUserManager):
def create_user(self, username, password, **extra_fields):
if not username:
raise ValueError(_('The username must be set'))
user = self.model(username=username, **extra_fields)
user.set_password(password)
user.save()
return user
def create_superuser(self, username, password, **extra_fields):
"""
Create and save a SuperUser with the given email and password.
"""
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
if extra_fields.get('is_staff') is not True:
raise ValueError(_('Superuser must have is_staff=True.'))
if extra_fields.get('is_superuser') is not True:
raise ValueError(_('Superuser must have is_superuser=True.'))
return self.create_user(username, password, **extra_fields)

View File

@ -0,0 +1,59 @@
# Generated by Django 3.2.8 on 2021-10-24 11: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
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='CustomGroup',
fields=[
('group_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='auth.group')),
],
options={
'verbose_name': 'Permissions group',
'verbose_name_plural': 'Permissions groups',
},
bases=('auth.group',),
managers=[
('objects', django.contrib.auth.models.GroupManager()),
],
),
migrations.CreateModel(
name='CustomUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('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')),
('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')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
),
]

View File

@ -0,0 +1,26 @@
from django.db import models
from django.contrib.auth.models import AbstractUser, Group
from .managers import CustomUserManager
class CustomUser(AbstractUser):
mobile = models.CharField(max_length=15, null=True, blank=True, unique=True,
help_text='Users mobile phone')
verification_code = models.CharField(max_length=10, unique=True, null=True, blank=True,
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')
email = models.EmailField(max_length=30, unique=True, blank=False, null=True, help_text='User email')
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
objects = CustomUserManager()
def __str__(self):
return self.username
class CustomGroup(Group):
class Meta:
verbose_name = 'Permissions group'
verbose_name_plural = 'Permissions groups'

View File

@ -0,0 +1,16 @@
from celery import shared_task
from django.core.mail import send_mail
from django.conf import settings
from django.template.loader import render_to_string
@shared_task
def mail_send(to_email: str, subject: str, text_content: str = '', **kwargs) -> None:
username = kwargs.get('username')
from_email = settings.DEFAULT_FROM_EMAIL
msg_html = render_to_string('registration/message.html',
{'username': username})
send_mail(subject, text_content, from_email, [to_email], html_message=msg_html, fail_silently=False)

View File

@ -0,0 +1,3 @@
{% block content %}
{% endblock %}

View File

@ -0,0 +1,120 @@
{% 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;
}
.form-control {
height: 41px;
background: #f2f2f2;
box-shadow: none !important;
border: none;
}
.form-control:focus {
background: #e2e2e2;
}
.form-control, .btn {
border-radius: 3px;
}
.signup-form {
width: 400px;
margin: 30px auto;
}
.signup-form form {
color: #999;
border-radius: 3px;
margin-bottom: 15px;
background: #fff;
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
padding: 30px;
}
.signup-form h2 {
color: #333;
font-weight: bold;
margin-top: 0;
}
.signup-form hr {
margin: 0 -30px 20px;
}
.signup-form .form-group {
margin-bottom: 20px;
}
.signup-form input[type="checkbox"] {
margin-top: 3px;
}
.signup-form .row div:first-child {
padding-right: 10px;
}
.signup-form .row div:last-child {
padding-left: 10px;
}
.signup-form .btn {
font-size: 16px;
font-weight: bold;
background: #3598dc;
border: none;
min-width: 140px;
}
.signup-form .btn:hover, .signup-form .btn:focus {
background: #2389cd !important;
outline: none;
}
.signup-form a {
color: #fff;
text-decoration: underline;
}
.signup-form a:hover {
text-decoration: none;
}
.signup-form form a {
color: #3598dc;
text-decoration: none;
}
.signup-form form a:hover {
text-decoration: underline;
}
.signup-form .hint-text {
padding-bottom: 15px;
text-align: center;
}
</style>
<div class="signup-form">
<form method="post">
<h2>Login</h2>
<p>Please fill in this form to Log in</p>
<hr>
{% csrf_token %}
<div class="form-group">
<input type="text" class="form-control" name="username" placeholder="Username" required="required">
</div>
<div class="form-group">
<input type="password" class="form-control" name="password" placeholder="Password" required="required">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-lg">Login</button>
</div>
<div class="form-group">
{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
<li>{{ error }}</li>
{% endfor %}
{% endif %}
</div>
</form>
<div class="hint-text">Don't have an account? <a href="{% url 'accounts:registration' %}">Register here</a></div>
</div>
{% endblock %}

View File

@ -0,0 +1,6 @@
<!doctype html>
<body>
<div>
<p style="color: green">Добро пожаловать, {{ username }}!</p>
</div>
</body>

View File

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block content %}
<h2>Password Changed successfully</h2>
<a href="{% url 'accounts:dashboard' %}">Back to dashboard</a>
{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block content %}
<h2>Password Change</h2>
<form method='POST'>
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Change">
</form>
<a href="{% url 'accounts:dashboard' %}">Back to dashboard</a>
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block content %}
Hello {{ user.username|default:'guest' }}!
<div>
{% if user.is_authenticated %}
<a href="{% url 'accounts:logout' %}">Logout</a>
<a href="{% url 'accounts:password_change' %}">Change Password</a>
<a href="{% url 'accounts:portfolio' %}">Go to Profile page</a>
{% else %}
<a href="{% url 'accounts:login' %}">Login</a>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,154 @@
{% 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;
}
.form-control {
height: 41px;
background: #f2f2f2;
box-shadow: none !important;
border: none;
}
.form-control:focus {
background: #e2e2e2;
}
.form-control, .btn {
border-radius: 3px;
}
.signup-form {
width: 400px;
margin: 30px auto;
}
.signup-form form {
color: #999;
border-radius: 3px;
margin-bottom: 15px;
background: #fff;
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
padding: 30px;
}
.signup-form h2 {
color: #333;
font-weight: bold;
margin-top: 0;
}
.signup-form hr {
margin: 0 -30px 20px;
}
.signup-form .form-group {
margin-bottom: 20px;
}
.signup-form input[type="checkbox"] {
margin-top: 3px;
}
.signup-form .row div:first-child {
padding-right: 10px;
}
.signup-form .row div:last-child {
padding-left: 10px;
}
.signup-form .btn {
font-size: 16px;
font-weight: bold;
background: #3598dc;
border: none;
min-width: 140px;
}
.signup-form .btn:hover, .signup-form .btn:focus {
background: #2389cd !important;
outline: none;
}
.signup-form a {
color: #fff;
text-decoration: underline;
}
.signup-form a:hover {
text-decoration: none;
}
.signup-form form a {
color: #3598dc;
text-decoration: none;
}
.signup-form form a:hover {
text-decoration: underline;
}
.signup-form .hint-text {
padding-bottom: 15px;
text-align: center;
}
</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 %}
<div class="form-group">
{% 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 class="form-group">{{ form.username.errors }}</div>
</div>
<div class="form-group">
<div class="row">
{% if form.first_name.value %}
<div class="col"><input type="text" class="form-control" name="first_name" value="{{ form.first_name.value }}"></div>
{% else %}
<div class="col"><input type="text" class="form-control" name="first_name" placeholder="First Name" required="required"></div>
{% endif %}
{% if form.last_name.value %}
<div class="col"><input type="text" class="form-control" name="last_name" value="{{ form.last_name.value }}"></div>
{% else %}
<div class="col"><input type="text" class="form-control" name="last_name" placeholder="Last Name" required="required"></div>
{% endif %}
</div>
</div>
<div class="form-group">
{% if form.email.value %}
<input type="text" class="form-control" name="email" value="{{ form.email.value }}">
{% else %}
<input type="email" class="form-control" name="email" placeholder="Email" required="required">
{% endif %}
<div class="form-group">{{ form.email.errors }}</div>
</div>
<div class="form-group">
{% 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" >
{% endif %}
<label for="mobile">Not required</label>
<div class="form-group">{{ form.mobile.errors }}</div>
</div>
<div class="form-group">
<input type="password" class="form-control" name="password1" placeholder="Password" required="required">
</div>
<div class="form-group">
<input type="password" class="form-control" name="password2" placeholder="Confirm Password" required="required">
<div class="form-group">{{ form.password2.errors }}</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-lg">Sign Up</button>
</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>
</div>
{% endblock content %}

View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@ -0,0 +1,10 @@
from django.urls import path, include
from .views import dashboard, RegisterUser
app_name = 'accounts'
urlpatterns = [
path('', include("django.contrib.auth.urls")),
path('dashboard/', dashboard, name='dashboard'),
path('registration/', RegisterUser.as_view(), name='registration')
]

View File

@ -0,0 +1,34 @@
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 server.apps.accounts.forms import CustomUserCreationForm
from django.core.validators import validate_email
from .tasks import mail_send
# Create your views here.
def dashboard(request):
return render(request, "users/dashboard.html", {})
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')