From 72334e099cf0abc2978f23431be0e7524ac3ee44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Thu, 31 Aug 2023 13:36:09 +0300 Subject: [PATCH 01/22] =?UTF-8?q?=D0=A3=D1=82=D0=B8=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8B=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D1=8B=20=D1=81=20=D1=80=D0=B0=D1=81=D1=81=D1=8B=D0=BB=D0=BA?= =?UTF-8?q?=D0=BE=D0=B9=20email=20=D0=B8=20=D1=80=D0=B5=D0=B3=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D1=86=D0=B8=D0=B5=D0=B9=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D0=B5=D0=B9=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=B2=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20users.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/users/admin.py | 3 +-- src/users/forms.py | 2 +- src/users/signals.py | 2 +- src/{utils/emailing => users/utils}/__init__.py | 0 src/{utils/users => users/utils/emailing}/__init__.py | 0 src/{ => users}/utils/emailing/render.py | 0 src/{ => users}/utils/emailing/reset_password.py | 2 +- src/utils/users/registration.py | 8 -------- 8 files changed, 4 insertions(+), 13 deletions(-) rename src/{utils/emailing => users/utils}/__init__.py (100%) rename src/{utils/users => users/utils/emailing}/__init__.py (100%) rename src/{ => users}/utils/emailing/render.py (100%) rename src/{ => users}/utils/emailing/reset_password.py (95%) delete mode 100644 src/utils/users/registration.py diff --git a/src/users/admin.py b/src/users/admin.py index a61d7462..4d732e4d 100644 --- a/src/users/admin.py +++ b/src/users/admin.py @@ -3,10 +3,9 @@ from django.contrib.auth.models import Group from django.utils.translation import gettext_lazy as _ -from utils.emailing.reset_password import send_password_reset_email - from .forms import UserChangeForm, UserCreationForm from .models import User +from .utils.emailing.reset_password import send_password_reset_email class UserAdmin(BaseUserAdmin): diff --git a/src/users/forms.py b/src/users/forms.py index 4edfd6f6..61351157 100644 --- a/src/users/forms.py +++ b/src/users/forms.py @@ -2,7 +2,7 @@ from django.contrib.auth.hashers import make_password from users.models import User -from utils.users.registration import generate_random_password +from users.utils.users.registration import generate_random_password class UserCreationForm(forms.ModelForm): diff --git a/src/users/signals.py b/src/users/signals.py index e4140e92..ec9203e4 100644 --- a/src/users/signals.py +++ b/src/users/signals.py @@ -2,7 +2,7 @@ from django.dispatch import receiver from users.models import User -from utils.emailing.reset_password import send_password_reset_email +from users.utils.emailing.reset_password import send_password_reset_email @receiver(post_save, sender=User) diff --git a/src/utils/emailing/__init__.py b/src/users/utils/__init__.py similarity index 100% rename from src/utils/emailing/__init__.py rename to src/users/utils/__init__.py diff --git a/src/utils/users/__init__.py b/src/users/utils/emailing/__init__.py similarity index 100% rename from src/utils/users/__init__.py rename to src/users/utils/emailing/__init__.py diff --git a/src/utils/emailing/render.py b/src/users/utils/emailing/render.py similarity index 100% rename from src/utils/emailing/render.py rename to src/users/utils/emailing/render.py diff --git a/src/utils/emailing/reset_password.py b/src/users/utils/emailing/reset_password.py similarity index 95% rename from src/utils/emailing/reset_password.py rename to src/users/utils/emailing/reset_password.py index 2585b0a7..858c8423 100644 --- a/src/utils/emailing/reset_password.py +++ b/src/users/utils/emailing/reset_password.py @@ -7,7 +7,7 @@ from config import settings from users.models import User -from utils.emailing.render import render_email_message +from users.utils.emailing.render import render_email_message def send_password_reset_email(instance: User, message=None, template=None): diff --git a/src/utils/users/registration.py b/src/utils/users/registration.py deleted file mode 100644 index ca732d52..00000000 --- a/src/utils/users/registration.py +++ /dev/null @@ -1,8 +0,0 @@ -import random -import string - - -def generate_random_password(length=12): - """Generate random password.""" - characters = string.ascii_letters + string.digits + string.punctuation - return "".join(random.choice(characters) for _ in range(length)) From 87b8ec273f12485c6318aac4333d73031cfe4015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Thu, 31 Aug 2023 13:38:52 +0300 Subject: [PATCH 02/22] =?UTF-8?q?=D0=A3=D1=82=D0=B8=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8B=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D1=8B=20=D1=81=20=D1=80=D0=B0=D1=81=D1=81=D1=8B=D0=BB=D0=BA?= =?UTF-8?q?=D0=BE=D0=B9=20email=20=D0=B8=20=D1=80=D0=B5=D0=B3=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D1=86=D0=B8=D0=B5=D0=B9=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D0=B5=D0=B9=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=B2=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20users.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/users/models.py | 13 +++++++++++++ src/users/utils/users/__init__.py | 0 2 files changed, 13 insertions(+) create mode 100644 src/users/utils/users/__init__.py diff --git a/src/users/models.py b/src/users/models.py index 4f001a31..bf34449f 100644 --- a/src/users/models.py +++ b/src/users/models.py @@ -2,6 +2,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ +from config import settings from core.models import Region from .manager import UserManager @@ -45,3 +46,15 @@ class UserRole(models.TextChoices): def __str__(self): return f"{self.first_name} {self.last_name}" + + +class UserTwoFactorAuthData(models.Model): + """Two-factor authentication data model.""" + + user = models.OneToOneField( + settings.AUTH_USER_MODEL, + related_name="two_factor_auth_data", + on_delete=models.CASCADE, + ) + + otp_secret = models.CharField(max_length=255) diff --git a/src/users/utils/users/__init__.py b/src/users/utils/users/__init__.py new file mode 100644 index 00000000..e69de29b From f73f30ed345e6f397b275646335a1cdea62d6089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 13:15:44 +0300 Subject: [PATCH 03/22] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D0=B8=20poetry.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poetry.lock | 19 ++++++++++++++++++- pyproject.toml | 1 + requirements/develop.txt | 1 + requirements/production.txt | 1 + 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 28e474cb..e49940a1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -354,6 +354,23 @@ django = ">=3.2" [package.extras] tests = ["coverage"] +[[package]] +name = "django-otp" +version = "1.2.2" +description = "A pluggable framework for adding two-factor authentication to Django using one-time passwords." +optional = false +python-versions = ">=3.7" +files = [ + {file = "django_otp-1.2.2-py3-none-any.whl", hash = "sha256:90765d5dac238a719f9550ac05681dd6307f513a81a10b6adb879b4abc6bc1a3"}, + {file = "django_otp-1.2.2.tar.gz", hash = "sha256:007a6354dabb3a1a54574bf73abf045ebbde0bb8734a38e2ed7845ba450f345e"}, +] + +[package.dependencies] +django = ">=3.2" + +[package.extras] +qrcode = ["qrcode"] + [[package]] name = "dnspython" version = "2.4.2" @@ -1296,4 +1313,4 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [metadata] lock-version = "2.0" python-versions = "^3.11.1" -content-hash = "03a56e6a6d84f459588eb5f27d47be1b795f02df1a398a5c91c707645e14b84a" +content-hash = "3d36380363be3a867d55e3ba155bcdaecb575e54e8e153317f34b47e9e782aaa" diff --git a/pyproject.toml b/pyproject.toml index 607f51b5..fed4c03a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ pytest-django = "^4.5.2" pytest-asyncio = "^0.21.1" flake8-docstrings = "^1.7.0" django-ckeditor = "^6.7.0" +django-otp = "^1.2.2" [tool.poetry.group.dev.dependencies] diff --git a/requirements/develop.txt b/requirements/develop.txt index 745fafde..bf13696f 100644 --- a/requirements/develop.txt +++ b/requirements/develop.txt @@ -14,6 +14,7 @@ django-asgi-lifespan==0.1.0 ; python_full_version >= "3.11.1" and python_version django-ckeditor==6.7.0 ; python_full_version >= "3.11.1" and python_full_version < "4.0.0" django-environ==0.10.0 ; python_full_version >= "3.11.1" and python_version < "4" django-js-asset==2.1.0 ; python_full_version >= "3.11.1" and python_full_version < "4.0.0" +django-otp==1.2.2 ; python_full_version >= "3.11.1" and python_full_version < "4.0.0" django==4.2.4 ; python_full_version >= "3.11.1" and python_version < "4.0" dnspython==2.4.2 ; python_full_version >= "3.11.1" and python_version < "4.0" email-validator==2.0.0.post2 ; python_full_version >= "3.11.1" and python_full_version < "4.0.0" diff --git a/requirements/production.txt b/requirements/production.txt index 9be3fe6e..58c9865f 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -11,6 +11,7 @@ django-asgi-lifespan==0.1.0 ; python_full_version >= "3.11.1" and python_version django-ckeditor==6.7.0 ; python_full_version >= "3.11.1" and python_full_version < "4.0.0" django-environ==0.10.0 ; python_full_version >= "3.11.1" and python_version < "4" django-js-asset==2.1.0 ; python_full_version >= "3.11.1" and python_full_version < "4.0.0" +django-otp==1.2.2 ; python_full_version >= "3.11.1" and python_full_version < "4.0.0" django==4.2.4 ; python_full_version >= "3.11.1" and python_version < "4.0" dnspython==2.4.2 ; python_full_version >= "3.11.1" and python_version < "4.0" email-validator==2.0.0.post2 ; python_full_version >= "3.11.1" and python_full_version < "4.0.0" From f71e3a0eea256e327f907dc5cb6d218bb4897995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 13:25:28 +0300 Subject: [PATCH 04/22] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BA=D0=B0=D1=81=D1=82=D0=BE=D0=BC=D0=BD=D1=8B?= =?UTF-8?q?=D0=B9=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=20=D0=B2=D1=85?= =?UTF-8?q?=D0=BE=D0=B4=D0=B0=20=D0=B2=20=D0=B0=D0=B4=D0=BC=D0=B8=D0=BD-?= =?UTF-8?q?=D0=BF=D0=B0=D0=BD=D0=B5=D0=BB=D1=8C.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/templates/login.html | 95 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/templates/login.html diff --git a/src/templates/login.html b/src/templates/login.html new file mode 100644 index 00000000..1b911aa7 --- /dev/null +++ b/src/templates/login.html @@ -0,0 +1,95 @@ +{% extends "admin/base_site.html" %} +{% load i18n static %} + +{% block extrastyle %} + {{ block.super }} + + {{ form.media }} + + +{% endblock %} + +{% block bodyclass %}{{ block.super }} login{% endblock %} + +{% block usertools %}{% endblock %} + +{% block nav-global %}{% endblock %} + +{% block content_title %}{% endblock %} + +{% block breadcrumbs %}{% endblock %} + +{% block nav-sidebar %}{% endblock %} + +{% block content %} +{% if form.errors and not form.non_field_errors %} +

+{% if form.errors.items|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %} +

+{% endif %} + +{% if form.non_field_errors %} +{% for error in form.non_field_errors %} +

+ {{ error }} +

+{% endfor %} +{% endif %} + +
+ +{% if user.is_authenticated %} +

+{% blocktrans trimmed %} + You are authenticated as {{ username }}, but are not authorized to + access this page. Would you like to login to a different account? +{% endblocktrans %} +

+{% endif %} + +
{% csrf_token %} +
+ {{ form.username.errors }} + {{ form.username.label_tag }} {{ form.username }} +
+
+ {{ form.password.errors }} + {{ form.password.label_tag }} {{ form.password }} + +
+ {% if form.get_user %} +
+ {{ form.otp_token.errors }} + {{ form.otp_token }} +
+ {% endif %} + {% url 'admin_password_reset' as password_reset_url %} + {% if password_reset_url %} + + {% endif %} +
+ + {% if form.get_user %} + + {% endif %} +
+
+ + +
+{% endblock %} From cfdc8da29fa68f0731b469537c2e9936b1b89406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 13:25:53 +0300 Subject: [PATCH 05/22] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BA=D0=B0=D1=81=D1=82=D0=BE=D0=BC=D0=BD=D1=8B?= =?UTF-8?q?=D0=B9=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=20=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D1=8C=D0=BC=D0=B0=20=D1=81=20=D0=BE=D0=B4=D0=BD=D0=BE?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=D0=BE=D0=B2=D1=8B=D0=BC=20=D0=BA=D0=BE=D0=B4?= =?UTF-8?q?=D0=BE=D0=BC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/templates/users/emailing/otp_email.html | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/templates/users/emailing/otp_email.html diff --git a/src/templates/users/emailing/otp_email.html b/src/templates/users/emailing/otp_email.html new file mode 100644 index 00000000..7a946cb6 --- /dev/null +++ b/src/templates/users/emailing/otp_email.html @@ -0,0 +1,15 @@ +{% extends "users/emailing/email.html" %} +{% block subject %}Одноразовый код для доступа админ-панели "Расправь Крылья"{% endblock %} +{% block content %} +
+ {% if message %} +

{{ message }}

+ {% else %} +

Для подтверждения ваших учетных данных используйте код:

+ {% endif %} +
+

{{ token }}

+
+

Никому его не сообщайте.

+
+{% endblock %} From ad9d938bdf5c45b3470461735d4fee0f948baead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 13:27:59 +0300 Subject: [PATCH 06/22] =?UTF-8?q?=D0=A8=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD?= =?UTF-8?q?=D1=8B=20=D1=80=D0=B0=D0=B7=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=D1=8B?= =?UTF-8?q?=20=D0=BF=D0=BE=20=D1=81=D0=BE=D0=BE=D1=82=D0=B2=D0=B5=D1=82?= =?UTF-8?q?=D1=81=D1=82=D0=B2=D1=83=D1=8E=D1=89=D0=B8=D0=BC=20=D0=B4=D0=B8?= =?UTF-8?q?=D1=80=D0=B5=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D1=8F=D0=BC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/templates/{ => users/authentication}/login.html | 0 src/templates/{ => users/emailing}/email.html | 0 src/templates/{ => users/emailing}/password_reset_email.html | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename src/templates/{ => users/authentication}/login.html (100%) rename src/templates/{ => users/emailing}/email.html (100%) rename src/templates/{ => users/emailing}/password_reset_email.html (91%) diff --git a/src/templates/login.html b/src/templates/users/authentication/login.html similarity index 100% rename from src/templates/login.html rename to src/templates/users/authentication/login.html diff --git a/src/templates/email.html b/src/templates/users/emailing/email.html similarity index 100% rename from src/templates/email.html rename to src/templates/users/emailing/email.html diff --git a/src/templates/password_reset_email.html b/src/templates/users/emailing/password_reset_email.html similarity index 91% rename from src/templates/password_reset_email.html rename to src/templates/users/emailing/password_reset_email.html index 487d671a..e56cd256 100644 --- a/src/templates/password_reset_email.html +++ b/src/templates/users/emailing/password_reset_email.html @@ -1,4 +1,4 @@ -{% extends "email.html" %} +{% extends "users/emailing/email.html" %} {% block subject %}Доступ к админ-панели бота фонда "Расправь Крылья"{% endblock %} {% block content %}
From 14bb9b460be14af45d512983435a547758c2a224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 13:31:26 +0300 Subject: [PATCH 07/22] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=B4=D0=BB=D1=8F=202FA.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/settings.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/config/settings.py b/src/config/settings.py index 562ee1ad..6c45e6ee 100644 --- a/src/config/settings.py +++ b/src/config/settings.py @@ -25,6 +25,8 @@ "users.apps.UsersConfig", "core.apps.CoreConfig", "ckeditor", + "django_otp", + "django_otp.plugins.otp_email", ] MIDDLEWARE = [ @@ -33,6 +35,7 @@ "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", + "django_otp.middleware.OTPMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] @@ -124,6 +127,9 @@ EMAIL_USE_SSL = True DEFAULT_RECEIVER = env.str("DEFAULT_EMAIL_ADDRESS") +OTP_EMAIL_SENDER = EMAIL_HOST_USER +OTP_EMAIL_BODY_HTML_TEMPLATE_PATH = "users/emailing/otp_email.html" + # Telegram bot settings TELEGRAM_TOKEN = env.str("TELEGRAM_TOKEN") USE_REDIS_PERSISTENCE = env.bool("REDIS", default=False) From 53d2aa1e0374666f71d23c24d0dbc5f01f77c2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 13:33:10 +0300 Subject: [PATCH 08/22] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8=20EmailDevice=20?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D1=8B=D1=85=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D0=B5=D0=B9=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=80=D0=B0=D1=81=D1=81=D1=8B=D0=BB=D0=BA=D0=B8=20OTP?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/users/signals.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/users/signals.py b/src/users/signals.py index ec9203e4..33f79b6a 100644 --- a/src/users/signals.py +++ b/src/users/signals.py @@ -1,5 +1,6 @@ from django.db.models.signals import post_save from django.dispatch import receiver +from django_otp.plugins.otp_email.models import EmailDevice from users.models import User from users.utils.emailing.reset_password import send_password_reset_email @@ -8,5 +9,10 @@ @receiver(post_save, sender=User) def password_reset_email(sender, instance, created, **kwargs): """Send email to new admin with link to set password.""" - if created and not instance.is_superuser: - send_password_reset_email(instance) + if created: + EmailDevice.objects.create( + user=instance, + email=instance.email, + ) + if not instance.is_superuser: + send_password_reset_email(instance) From 493ce79e2cb649a989515fb77d30adce814ac953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 13:35:24 +0300 Subject: [PATCH 09/22] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BA=D0=B0=D1=81=D1=82=D0=BE=D0=BC=D0=BD=D1=8B?= =?UTF-8?q?=D0=B9=20=D1=81=D0=B0=D0=B9=D1=82=20=D0=B0=D0=B4=D0=BC=D0=B8?= =?UTF-8?q?=D0=BD-=D0=BF=D0=B0=D0=BD=D0=B5=D0=BB=D0=B8=20=D1=81=202FA.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/users/sites.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/users/sites.py diff --git a/src/users/sites.py b/src/users/sites.py new file mode 100644 index 00000000..e4bcb333 --- /dev/null +++ b/src/users/sites.py @@ -0,0 +1,10 @@ +from django_otp.admin import OTPAdminSite + +from users.forms import CustomOTPAuthenticationForm + + +class CustomOTPAdminSite(OTPAdminSite): + """Customized admin site.""" + + login_form = CustomOTPAuthenticationForm + login_template = "users/authentication/login.html" From 2dd442c8f526f6d6a0e4d648cb664463088862e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 13:36:57 +0300 Subject: [PATCH 10/22] =?UTF-8?q?=D0=97=D0=B0=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=B4=D0=B5=D1=84=D0=BE=D0=BB=D1=82=D0=BD=D1=8B=D0=B9?= =?UTF-8?q?=20=D1=81=D0=B0=D0=B9=D1=82=20=D0=B0=D0=B4=D0=BC=D0=B8=D0=BD-?= =?UTF-8?q?=D0=BF=D0=B0=D0=BD=D0=B5=D0=BB=D0=B8=20=D0=BD=D0=B0=20=D0=BA?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D0=BE=D0=BC=D0=B8=D0=B7=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=BD=D1=8B=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/urls.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config/urls.py b/src/config/urls.py index c6e31d51..391d8cd2 100644 --- a/src/config/urls.py +++ b/src/config/urls.py @@ -3,6 +3,10 @@ from django.contrib import admin from django.urls import include, path +from users.sites import CustomOTPAdminSite + +admin.site.__class__ = CustomOTPAdminSite + urlpatterns = [ path("users/", include("users.urls"), name="users"), path("bot/", include("bot.urls"), name="bot"), From 74ab09efd472774e34ab879c28303cea31170f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 13:40:19 +0300 Subject: [PATCH 11/22] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=202FA.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/users/forms.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/users/forms.py b/src/users/forms.py index 61351157..e15f740b 100644 --- a/src/users/forms.py +++ b/src/users/forms.py @@ -1,5 +1,10 @@ from django import forms +from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.hashers import make_password +from django.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext_lazy +from django_otp.forms import OTPAuthenticationFormMixin +from django_otp.plugins.otp_email.models import EmailDevice from users.models import User from users.utils.users.registration import generate_random_password @@ -33,3 +38,44 @@ class Meta: "is_active", "is_superuser", ) + + +class CustomOTPAuthenticationFormMixin(OTPAuthenticationFormMixin): + """Customized OTPAuthenticationFormMixin mixin.""" + + otp_error_messages = { + "token_required": _("Пожалуйста, введите одноразовый код."), + "challenge_exception": _("Ошибка генерации кода: {0}"), + "not_interactive": _("Код не может быть отправлен."), + "challenge_message": _("Код отправлен на указанную почту."), + "invalid_token": _("Неверный код. Проверьте правильность."), + "n_failed_attempts": ngettext_lazy( + "Допущено %(failure_count) ошибок. Доступ временно ограничен.", + "Допущено %(failure_count) ошибок. Доступ временно ограничен.", + "failure_count", + ), + "verification_not_allowed": _("Верификация недоступна"), + } + + def _chosen_device(self, user): + """Return EmailDevise as default.""" + return EmailDevice.objects.filter(user=user).first() + + +class CustomOTPAuthenticationForm( + CustomOTPAuthenticationFormMixin, AuthenticationForm +): + """Customized OTPAuthenticationForm form.""" + + otp_token = forms.CharField( + required=False, widget=forms.TextInput(attrs={"autocomplete": "off"}) + ) + + otp_challenge = forms.CharField(required=False) + + def clean(self): + """Clean form data.""" + self.cleaned_data = super().clean() + self.clean_otp(self.get_user()) + + return self.cleaned_data From 14cc3f303750d2475b95d3cf9179d3b49708eb8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 13:40:47 +0300 Subject: [PATCH 12/22] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=BD=D0=B5=D0=BD=D1=83=D0=B6=D0=BD=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/users/models.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/users/models.py b/src/users/models.py index bf34449f..4f001a31 100644 --- a/src/users/models.py +++ b/src/users/models.py @@ -2,7 +2,6 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from config import settings from core.models import Region from .manager import UserManager @@ -46,15 +45,3 @@ class UserRole(models.TextChoices): def __str__(self): return f"{self.first_name} {self.last_name}" - - -class UserTwoFactorAuthData(models.Model): - """Two-factor authentication data model.""" - - user = models.OneToOneField( - settings.AUTH_USER_MODEL, - related_name="two_factor_auth_data", - on_delete=models.CASCADE, - ) - - otp_secret = models.CharField(max_length=255) From adacfe939e5fde5c6ef689fe23ebfacdca0fa86d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 13:41:21 +0300 Subject: [PATCH 13/22] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=83=D1=82=D0=B8=20=D0=BA=20=D1=88?= =?UTF-8?q?=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=D0=B0=D0=BC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/users/utils/emailing/reset_password.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/users/utils/emailing/reset_password.py b/src/users/utils/emailing/reset_password.py index 858c8423..d7f25969 100644 --- a/src/users/utils/emailing/reset_password.py +++ b/src/users/utils/emailing/reset_password.py @@ -13,7 +13,7 @@ def send_password_reset_email(instance: User, message=None, template=None): """Send email with password reset link.""" if template is None: - template = "password_reset_email.html" + template = "users/emailing/password_reset_email.html" reset_link = get_password_reset_link(instance) email = render_email_message( subject='Доступ к админ-панели бота "Расправь крылья"', From 0f29187d9974c1cc1b0b3f15effb7d1f5c6659bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 13:41:51 +0300 Subject: [PATCH 14/22] =?UTF-8?q?=D0=A3=D1=82=D0=B8=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8B=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D1=8B=20=D1=81=20=D1=80=D0=B0=D1=81=D1=81=D1=8B=D0=BB=D0=BA?= =?UTF-8?q?=D0=BE=D0=B9=20email=20=D0=B8=20=D1=80=D0=B5=D0=B3=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D1=86=D0=B8=D0=B5=D0=B9=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D0=B5=D0=B9=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=B2=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20users.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/users/utils/users/registration.py | 8 ++++++++ src/users/views.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 src/users/utils/users/registration.py diff --git a/src/users/utils/users/registration.py b/src/users/utils/users/registration.py new file mode 100644 index 00000000..ca732d52 --- /dev/null +++ b/src/users/utils/users/registration.py @@ -0,0 +1,8 @@ +import random +import string + + +def generate_random_password(length=12): + """Generate random password.""" + characters = string.ascii_letters + string.digits + string.punctuation + return "".join(random.choice(characters) for _ in range(length)) diff --git a/src/users/views.py b/src/users/views.py index 56e678ec..d2e2800c 100644 --- a/src/users/views.py +++ b/src/users/views.py @@ -12,7 +12,7 @@ class PasswordSetView(PasswordResetConfirmView): def form_valid(self, form): """Set user satus as active if password was changed.""" response = super().form_valid(form) - messages.success(self.request, "Ваш пароль был успешно изменен.") + messages.success(self.request, "Пароль был успешно изменен.") self.user.is_staff = True self.user.save() return response From 25efb2bcbb3f5626ece53da334a57bc8c1f1798b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 13:59:01 +0300 Subject: [PATCH 15/22] =?UTF-8?q?=D0=A8=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D0=B5=D1=81=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=B2=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20users.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 11 ++++++++--- src/config/settings.py | 2 +- src/users/sites.py | 2 +- .../templates}/authentication/login.html | 0 .../{ => authentication}/password_set_confirm.html | 0 .../users => users/templates}/emailing/email.html | 0 .../users => users/templates}/emailing/otp_email.html | 2 +- .../templates}/emailing/password_reset_email.html | 2 +- src/users/utils/emailing/reset_password.py | 2 +- src/users/views.py | 2 +- 10 files changed, 14 insertions(+), 9 deletions(-) rename src/{templates/users => users/templates}/authentication/login.html (100%) rename src/users/templates/{ => authentication}/password_set_confirm.html (100%) rename src/{templates/users => users/templates}/emailing/email.html (100%) rename src/{templates/users => users/templates}/emailing/otp_email.html (93%) rename src/{templates/users => users/templates}/emailing/password_reset_email.html (91%) diff --git a/Makefile b/Makefile index e0451651..0fbd0487 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ help: # Show help .PHONY: runbot-init -runbot-init: deletedb rundb migrate filldb runbot-db # Build and run Database Docker-image +runbot-init: deletedb rundb migrate filldb create_superuser runbot-db # Build and run Database Docker-image @echo -e "$(COLOR_YELLOW)Starting initialization...$(COLOR_RESET)" @source $$(poetry env info -p)/bin/activate @@ -99,7 +99,12 @@ run_tests: run_unit_tests # Run all tests .PHONY: run_unit_tests run_unit_tests: # Run unit tests @echo -e "$(COLOR_YELLOW)Start unit tests...$(COLOR_RESET)" - @cd src @poetry run pytest src/tests/unit - @cd .. @echo -e "$(COLOR_GREEN)Unit tests passed$(COLOR_RESET)" + + +.PHONY: create_superuser +create_superuser: # Run unit tests + @echo -e "$(COLOR_YELLOW)Creating superuser...$(COLOR_RESET)" + @poetry run python src/manage.py initadmin + @echo -e "$(COLOR_GREEN)Superuser created$(COLOR_RESET)" diff --git a/src/config/settings.py b/src/config/settings.py index 6c45e6ee..c4784502 100644 --- a/src/config/settings.py +++ b/src/config/settings.py @@ -128,7 +128,7 @@ DEFAULT_RECEIVER = env.str("DEFAULT_EMAIL_ADDRESS") OTP_EMAIL_SENDER = EMAIL_HOST_USER -OTP_EMAIL_BODY_HTML_TEMPLATE_PATH = "users/emailing/otp_email.html" +OTP_EMAIL_BODY_HTML_TEMPLATE_PATH = "emailing/otp_email.html" # Telegram bot settings TELEGRAM_TOKEN = env.str("TELEGRAM_TOKEN") diff --git a/src/users/sites.py b/src/users/sites.py index e4bcb333..533f9fcc 100644 --- a/src/users/sites.py +++ b/src/users/sites.py @@ -7,4 +7,4 @@ class CustomOTPAdminSite(OTPAdminSite): """Customized admin site.""" login_form = CustomOTPAuthenticationForm - login_template = "users/authentication/login.html" + login_template = "authentication/login.html" diff --git a/src/templates/users/authentication/login.html b/src/users/templates/authentication/login.html similarity index 100% rename from src/templates/users/authentication/login.html rename to src/users/templates/authentication/login.html diff --git a/src/users/templates/password_set_confirm.html b/src/users/templates/authentication/password_set_confirm.html similarity index 100% rename from src/users/templates/password_set_confirm.html rename to src/users/templates/authentication/password_set_confirm.html diff --git a/src/templates/users/emailing/email.html b/src/users/templates/emailing/email.html similarity index 100% rename from src/templates/users/emailing/email.html rename to src/users/templates/emailing/email.html diff --git a/src/templates/users/emailing/otp_email.html b/src/users/templates/emailing/otp_email.html similarity index 93% rename from src/templates/users/emailing/otp_email.html rename to src/users/templates/emailing/otp_email.html index 7a946cb6..76199e1d 100644 --- a/src/templates/users/emailing/otp_email.html +++ b/src/users/templates/emailing/otp_email.html @@ -1,4 +1,4 @@ -{% extends "users/emailing/email.html" %} +{% extends "emailing/email.html" %} {% block subject %}Одноразовый код для доступа админ-панели "Расправь Крылья"{% endblock %} {% block content %}
diff --git a/src/templates/users/emailing/password_reset_email.html b/src/users/templates/emailing/password_reset_email.html similarity index 91% rename from src/templates/users/emailing/password_reset_email.html rename to src/users/templates/emailing/password_reset_email.html index e56cd256..fc4a9bd0 100644 --- a/src/templates/users/emailing/password_reset_email.html +++ b/src/users/templates/emailing/password_reset_email.html @@ -1,4 +1,4 @@ -{% extends "users/emailing/email.html" %} +{% extends "emailing/email.html" %} {% block subject %}Доступ к админ-панели бота фонда "Расправь Крылья"{% endblock %} {% block content %}
diff --git a/src/users/utils/emailing/reset_password.py b/src/users/utils/emailing/reset_password.py index d7f25969..4d327eff 100644 --- a/src/users/utils/emailing/reset_password.py +++ b/src/users/utils/emailing/reset_password.py @@ -13,7 +13,7 @@ def send_password_reset_email(instance: User, message=None, template=None): """Send email with password reset link.""" if template is None: - template = "users/emailing/password_reset_email.html" + template = "emailing/password_reset_email.html" reset_link = get_password_reset_link(instance) email = render_email_message( subject='Доступ к админ-панели бота "Расправь крылья"', diff --git a/src/users/views.py b/src/users/views.py index d2e2800c..6bbf9183 100644 --- a/src/users/views.py +++ b/src/users/views.py @@ -7,7 +7,7 @@ class PasswordSetView(PasswordResetConfirmView): """User password reset view.""" success_url = reverse_lazy("admin:index") - template_name = "password_set_confirm.html" + template_name = "authentication/password_set_confirm.html" def form_valid(self, form): """Set user satus as active if password was changed.""" From 140ca4915077d4c47ebaa9adc5efdba0ac766b59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 14:18:14 +0300 Subject: [PATCH 16/22] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D1=82=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BC=D0=B8?= =?UTF-8?q?=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/test_settings.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/config/test_settings.py b/src/config/test_settings.py index 6fb3667b..f801b46a 100644 --- a/src/config/test_settings.py +++ b/src/config/test_settings.py @@ -24,6 +24,8 @@ "bot.apps.BotConfig", "users.apps.UsersConfig", "core.apps.CoreConfig", + "django_otp", + "django_otp.plugins.otp_email", ] MIDDLEWARE = [ @@ -32,6 +34,7 @@ "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", + "django_otp.middleware.OTPMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] @@ -56,7 +59,6 @@ WSGI_APPLICATION = "config.wsgi.application" - DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", From 15a8d41775aeee96bb2684bbbe2d147022ed9b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Fri, 1 Sep 2023 22:22:50 +0300 Subject: [PATCH 17/22] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=20=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D1=8C=D0=BC=D0=B0,=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=B2=D0=BE=D1=81=D0=BA=D0=BB=D0=B8?= =?UTF-8?q?=D1=86=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=B7?= =?UTF-8?q?=D0=BD=D0=B0=D0=BA.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/users/templates/emailing/otp_email.html | 2 +- src/users/templates/emailing/password_reset_email.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/users/templates/emailing/otp_email.html b/src/users/templates/emailing/otp_email.html index 76199e1d..a3684cfd 100644 --- a/src/users/templates/emailing/otp_email.html +++ b/src/users/templates/emailing/otp_email.html @@ -1,5 +1,5 @@ {% extends "emailing/email.html" %} -{% block subject %}Одноразовый код для доступа админ-панели "Расправь Крылья"{% endblock %} +{% block subject %}Одноразовый код для доступа админ-панели "Расправь Крылья!"{% endblock %} {% block content %}
{% if message %} diff --git a/src/users/templates/emailing/password_reset_email.html b/src/users/templates/emailing/password_reset_email.html index fc4a9bd0..68a97738 100644 --- a/src/users/templates/emailing/password_reset_email.html +++ b/src/users/templates/emailing/password_reset_email.html @@ -1,5 +1,5 @@ {% extends "emailing/email.html" %} -{% block subject %}Доступ к админ-панели бота фонда "Расправь Крылья"{% endblock %} +{% block subject %}Доступ к админ-панели бота фонда "Расправь Крылья!"{% endblock %} {% block content %}
{% if message %} From 79580fe2bb5813711a209b0eee5f47db3d78b677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Sat, 2 Sep 2023 00:21:47 +0300 Subject: [PATCH 18/22] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BE=D1=81=D0=BD=D0=BE=D0=B2=D0=B0=20=D1=88?= =?UTF-8?q?=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=D0=B0=20=D0=BD=D0=B0=20=D0=B2?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=BE=D0=B5=D0=BD=D0=BD=D1=8B=D0=B9=20base?= =?UTF-8?q?=5Fsite.html.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../authentication/password_set_confirm.html | 165 ++++++++++-------- 1 file changed, 88 insertions(+), 77 deletions(-) diff --git a/src/users/templates/authentication/password_set_confirm.html b/src/users/templates/authentication/password_set_confirm.html index 098c1027..a7404fb1 100644 --- a/src/users/templates/authentication/password_set_confirm.html +++ b/src/users/templates/authentication/password_set_confirm.html @@ -1,86 +1,97 @@ -{% extends "base.html" %} -{% load user_filters %} -{% block title %}Новый пароль{% endblock %} +{% extends "admin/base_site.html" %} +{% load i18n static %} + +{% block extrastyle %} +{{ block.super }} + +{{ form.media }} + + +{% endblock %} + +{% block bodyclass %}{{ block.super }} login{% endblock %} + +{% block branding %} +

Новый пароль

+{% if user.is_anonymous %} + {% include "admin/color_theme_toggle.html" %} +{% endif %} +{% endblock %} + +{% block usertools %}{% endblock %} + +{% block nav-global %}{% endblock %} + +{% block content_title %}{% endblock %} + +{% block breadcrumbs %}{% endblock %} + +{% block nav-sidebar %}{% endblock %} + {% block content %} {% if validlink %} -
- -
-
-
-
- {% include "includes/form_errors.html"%} -
- {% include "includes/form.html"%} -
-
-
+
+
+
+
+ {% if user.is_authenticated %} +

+ {% blocktrans trimmed %} + You are authenticated as {{ username }}, but are not + authorized to + access this page. Would you like to login to a + different account? + {% endblocktrans %} +

+ {% endif %} + + {% include "includes/form_errors.html"%} +
+ {% include "includes/form.html"%} +
+ +
-
-
-
-{% if messages %} -
    - {% for message in messages %} -
  • {{ - message }} -
  • - {% endfor %} -
-{% endif %} -{% else %} -
-
-
-
Ошибка
-
-

Ссылка установки пароля содержит ошибку или - устарела.

+ {% if messages %} +
    + {% for message in messages %} +
  • {{ message }}
  • + {% endfor %} +
+ {% endif %} + {% else %} +
+
+
+
Ошибка
+
+

Ссылка установки пароля содержит ошибку или + устарела.

+
+
+
+ {% endif %}
-{% endif %} - {% endblock %} From 27624fa3e387d41626473d4e70cca2d9d33b785b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Sat, 2 Sep 2023 00:22:13 +0300 Subject: [PATCH 19/22] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=20=D1=81=D1=82=D0=B8=D0=BB=D1=8C=20=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BE=D0=BA=20=D1=84=D0=BE=D1=80=D0=BC=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/users/templates/includes/form_errors.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/users/templates/includes/form_errors.html b/src/users/templates/includes/form_errors.html index 4688138f..93e47443 100644 --- a/src/users/templates/includes/form_errors.html +++ b/src/users/templates/includes/form_errors.html @@ -1,13 +1,13 @@ {% if form.errors %} {% for field in form %} -{% for error in field.errors %} -
- {{ error|escape }} -
-{% endfor %} + {% for error in field.errors %} +
+ {{ error|escape }} +
+ {% endfor %} {% endfor %} {% for error in form.non_field_errors %} -
+
{{ error|escape }}
{% endfor %} From 87aac47de14997fdb7a9a21ac93bffbb5705052c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Sat, 2 Sep 2023 00:23:22 +0300 Subject: [PATCH 20/22] =?UTF-8?q?=D0=9D=D0=B0=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=B9=D0=BA=D0=B8=20=D0=B0=D0=B4=D0=BC=D0=B8=D0=BD-=D0=BF?= =?UTF-8?q?=D0=B0=D0=BD=D0=B5=D0=BB=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BD?= =?UTF-8?q?=D0=B5=D1=81=D0=B5=D0=BD=D1=8B=20=D0=B8=D0=B7=20urls=20=D0=B2?= =?UTF-8?q?=20sites.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/urls.py | 2 -- src/users/sites.py | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/config/urls.py b/src/config/urls.py index 391d8cd2..85e367f3 100644 --- a/src/config/urls.py +++ b/src/config/urls.py @@ -14,5 +14,3 @@ ] urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) -admin.site.site_header = "Бот фонда 'Расправь крылья'" -admin.site.site_title = "Бот фонда 'Расправь крылья'" diff --git a/src/users/sites.py b/src/users/sites.py index 533f9fcc..ab67990f 100644 --- a/src/users/sites.py +++ b/src/users/sites.py @@ -7,4 +7,8 @@ class CustomOTPAdminSite(OTPAdminSite): """Customized admin site.""" login_form = CustomOTPAuthenticationForm + + site_header = "Бот фонда 'Расправь крылья'" + site_title = "Бот фонда 'Расправь крылья'" + login_template = "authentication/login.html" From 8419ff85b0f6153a4253b11cd335527ac84684f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Sat, 2 Sep 2023 00:23:49 +0300 Subject: [PATCH 21/22] =?UTF-8?q?=D0=9D=D0=B5=D0=BD=D1=83=D0=B6=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=D1=8B=20?= =?UTF-8?q?=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/users/templates/base.html | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/users/templates/base.html diff --git a/src/users/templates/base.html b/src/users/templates/base.html deleted file mode 100644 index cfd92eb5..00000000 --- a/src/users/templates/base.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - {% load static %} - - - - - - - - - {% block title %}{% endblock %} - - -Skip to main content - {% block content %}{% endblock %} - - From 3fbdfdaf1cda5fccbee707d27288bb7c763c1e20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D1=81?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8=D0=BD?= Date: Sun, 3 Sep 2023 19:25:06 +0300 Subject: [PATCH 22/22] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/bot_settings/migrations/0002_add_settings.py | 2 +- src/users/sites.py | 4 ++-- src/users/utils/emailing/reset_password.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e5f1a158..b90d63f6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Бот для фонда "Расправь крылья" +# Бот для фонда "Расправь крылья!" #### Проект телеграм-бота, который позволяет всем желающим и нуждающимся получить ответы на все необходимые вопросы в телеграме. [“Расправь крылья”](https://detskyfond.info/). diff --git a/src/bot_settings/migrations/0002_add_settings.py b/src/bot_settings/migrations/0002_add_settings.py index 7876496e..adabdbe1 100644 --- a/src/bot_settings/migrations/0002_add_settings.py +++ b/src/bot_settings/migrations/0002_add_settings.py @@ -29,7 +29,7 @@ def create_start_message_setting(apps, schema_editor): key="start_message", title="Сообщение при нажатии на кнопку Start", type=BotSettingsModel.TEXT, - value='Здравствуйте! Это бот фонда "Расправь крылья".', + value='Здравствуйте! Это бот фонда "Расправь крылья!".', ) diff --git a/src/users/sites.py b/src/users/sites.py index ab67990f..95c02d53 100644 --- a/src/users/sites.py +++ b/src/users/sites.py @@ -8,7 +8,7 @@ class CustomOTPAdminSite(OTPAdminSite): login_form = CustomOTPAuthenticationForm - site_header = "Бот фонда 'Расправь крылья'" - site_title = "Бот фонда 'Расправь крылья'" + site_header = "Бот фонда 'Расправь крылья!'" + site_title = "Бот фонда 'Расправь крылья!'" login_template = "authentication/login.html" diff --git a/src/users/utils/emailing/reset_password.py b/src/users/utils/emailing/reset_password.py index 4d327eff..d481cdd8 100644 --- a/src/users/utils/emailing/reset_password.py +++ b/src/users/utils/emailing/reset_password.py @@ -16,7 +16,7 @@ def send_password_reset_email(instance: User, message=None, template=None): template = "emailing/password_reset_email.html" reset_link = get_password_reset_link(instance) email = render_email_message( - subject='Доступ к админ-панели бота "Расправь крылья"', + subject='Доступ к админ-панели бота "Расправь крылья!"', context={"password_reset_link": reset_link, "message": message}, from_email=settings.EMAIL_HOST_USER, to=[