From a1d77d778afd3cce2c00b3c545c7131f5d265d70 Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Sat, 13 Dec 2025 01:25:19 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE=D0=BD=D0=B0?= =?UTF-8?q?=D0=BB=20=D0=B4=D0=B5=D0=B0=D0=BA=D1=82=D0=B8=D0=B2=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8/=D1=80=D0=B5=D0=B0=D0=BA=D1=82=D0=B8=D0=B2=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D1=80=D0=BE=D0=BB=D0=B5=D0=B9=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../myproject/admin_access_middleware.py | 16 +- .../user_roles/user_role_create.html | 127 +++++++++++++ .../user_roles/user_role_created.html | 117 ++++++++++++ .../user_roles/user_role_delete.html | 19 +- .../templates/user_roles/user_role_edit.html | 169 +++++++++++++++++- .../templates/user_roles/user_role_list.html | 43 ++++- .../user_roles/user_role_reactivate.html | 61 +++++++ myproject/user_roles/urls.py | 1 + myproject/user_roles/views.py | 168 +++++++++++++---- ГИД ПО ЗАПУСКУ | 5 + 10 files changed, 666 insertions(+), 60 deletions(-) create mode 100644 myproject/user_roles/templates/user_roles/user_role_created.html create mode 100644 myproject/user_roles/templates/user_roles/user_role_reactivate.html diff --git a/myproject/myproject/admin_access_middleware.py b/myproject/myproject/admin_access_middleware.py index 151cba9..17625a6 100644 --- a/myproject/myproject/admin_access_middleware.py +++ b/myproject/myproject/admin_access_middleware.py @@ -32,13 +32,15 @@ class TenantAdminAccessMiddleware: if hasattr(connection, 'tenant') and connection.tenant: # Проверяем: это не public схема? if connection.tenant.schema_name != 'public': - # Если пользователь авторизован, но НЕ суперпользователь - блокируем - if request.user.is_authenticated and not request.user.is_superuser: - return HttpResponseForbidden( - "Доступ запрещен. Только системные администраторы могут " - "заходить в админ-панель на поддоменах тенантов. " - "Используйте панель управления тенанта." - ) + # Проверяем наличие атрибута user (добавляется AuthenticationMiddleware) + if hasattr(request, 'user'): + # Если пользователь авторизован, но НЕ суперпользователь - блокируем + if request.user.is_authenticated and not request.user.is_superuser: + return HttpResponseForbidden( + "Доступ запрещен. Только системные администраторы могут " + "заходить в админ-панель на поддоменах тенантов. " + "Используйте панель управления тенанта." + ) response = self.get_response(request) return response diff --git a/myproject/user_roles/templates/user_roles/user_role_create.html b/myproject/user_roles/templates/user_roles/user_role_create.html index fd0dc21..facf44a 100644 --- a/myproject/user_roles/templates/user_roles/user_role_create.html +++ b/myproject/user_roles/templates/user_roles/user_role_create.html @@ -61,4 +61,131 @@ + + +{% if generated_password %} + + + +{% endif %} {% endblock %} diff --git a/myproject/user_roles/templates/user_roles/user_role_created.html b/myproject/user_roles/templates/user_roles/user_role_created.html new file mode 100644 index 0000000..2449524 --- /dev/null +++ b/myproject/user_roles/templates/user_roles/user_role_created.html @@ -0,0 +1,117 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}Пользователь создан{% endblock %} + +{% block content %} +
+
+
+
+
+

+ Пользователь успешно создан +

+
+
+
+ + Важно! Сохраните эти данные - пароль больше не будет показан. +
+ +
+
Данные для входа:
+ + + + + + + + + + + + + + + +
Email (логин): + {{ created_user_email }} + +
Пароль: + {{ generated_password }} + +
Роль:{{ user_role }}
+
+ +
+ + + Формат: email / password + +
+ + +
+
+
+
+
+ + +{% endblock %} diff --git a/myproject/user_roles/templates/user_roles/user_role_delete.html b/myproject/user_roles/templates/user_roles/user_role_delete.html index 9b750c8..ff646ab 100644 --- a/myproject/user_roles/templates/user_roles/user_role_delete.html +++ b/myproject/user_roles/templates/user_roles/user_role_delete.html @@ -1,19 +1,19 @@ {% extends "base.html" %} -{% block title %}Удалить пользователя{% endblock %} +{% block title %}Деактивировать пользователя{% endblock %} {% block content %}
-
-
-

Удалить доступ пользователя

+
+
+

Деактивировать пользователя

- Внимание! Вы собираетесь удалить доступ пользователя к системе. + Внимание! Пользователь будет деактивирован, но его данные останутся в системе.
@@ -40,16 +40,17 @@

- Пользователь больше не сможет войти в систему через этот магазин. - Учетная запись пользователя не будет удалена. + Пользователь больше не сможет войти в систему. + Все его данные (заказы, история действий) сохранятся. + При необходимости вы сможете реактивировать пользователя.

{% csrf_token %}
Отмена -
diff --git a/myproject/user_roles/templates/user_roles/user_role_edit.html b/myproject/user_roles/templates/user_roles/user_role_edit.html index b41ca75..5ffd098 100644 --- a/myproject/user_roles/templates/user_roles/user_role_edit.html +++ b/myproject/user_roles/templates/user_roles/user_role_edit.html @@ -21,12 +21,20 @@ {% endif %}
- -

{{ user_role.user.email }} ({{ user_role.user.name }})

+ +

{{ user_role.user.email }}

{% csrf_token %} + + +
+ + +
Полное имя пользователя
+
@@ -50,14 +58,167 @@
Снимите галочку, чтобы временно отключить доступ
-
+
Отмена - +
+ + +
+ + +
+ + +{% if generated_password %} + + + +{% endif %} {% endblock %} diff --git a/myproject/user_roles/templates/user_roles/user_role_list.html b/myproject/user_roles/templates/user_roles/user_role_list.html index 8ce4671..48ef7a3 100644 --- a/myproject/user_roles/templates/user_roles/user_role_list.html +++ b/myproject/user_roles/templates/user_roles/user_role_list.html @@ -20,6 +20,25 @@ {% endfor %} {% endif %} + +
+
+
+
+
+ + +
+
+
+
+
+
{% if user_roles %} @@ -52,10 +71,10 @@ - {% if user_role.is_active %} - Активен - {% else %} + {% if not user_role.is_active or not user_role.user.is_active %} Неактивен + {% else %} + Активен {% endif %} {{ user_role.created_at|date:"d.m.Y H:i" }} @@ -68,12 +87,18 @@ {% if user_role.user != request.user %} - - Изменить - - - Удалить - + {% if user_role.is_active and user_role.user.is_active %} + + Изменить + + + Деактивировать + + {% else %} + + Реактивировать + + {% endif %} {% else %} Ваша роль diff --git a/myproject/user_roles/templates/user_roles/user_role_reactivate.html b/myproject/user_roles/templates/user_roles/user_role_reactivate.html new file mode 100644 index 0000000..ac99574 --- /dev/null +++ b/myproject/user_roles/templates/user_roles/user_role_reactivate.html @@ -0,0 +1,61 @@ +{% extends "base.html" %} + +{% block title %}Реактивировать пользователя{% endblock %} + +{% block content %} +
+
+
+
+
+

Реактивировать пользователя

+
+
+
+ + Информация: Пользователь снова получит доступ к системе. +
+ +
+
Email:
+
{{ user_role.user.email }}
+ +
Имя:
+
{{ user_role.user.name }}
+ +
Роль:
+
+ + {{ user_role.role.name }} + +
+ +
Был деактивирован:
+
{{ user_role.created_at|date:"d.m.Y H:i" }}
+
+ +

+ Пользователь сможет снова войти в систему с прежней ролью. + Все его данные (заказы, история действий) сохранены. +

+ +
+ {% csrf_token %} +
+ Отмена + +
+
+
+
+
+
+
+{% endblock %} diff --git a/myproject/user_roles/urls.py b/myproject/user_roles/urls.py index 6d39bcc..3bc7e97 100644 --- a/myproject/user_roles/urls.py +++ b/myproject/user_roles/urls.py @@ -8,4 +8,5 @@ urlpatterns = [ path('create/', views.user_role_create, name='create'), path('/edit/', views.user_role_edit, name='edit'), path('/delete/', views.user_role_delete, name='delete'), + path('/reactivate/', views.user_role_reactivate, name='reactivate'), ] diff --git a/myproject/user_roles/views.py b/myproject/user_roles/views.py index ecf18ee..93a69c2 100644 --- a/myproject/user_roles/views.py +++ b/myproject/user_roles/views.py @@ -13,12 +13,24 @@ User = get_user_model() @owner_required def user_role_list(request): """Список пользователей с их ролями""" - user_roles = UserRole.objects.select_related('user', 'role', 'created_by').all() + # Фильтр по активности + show_inactive = request.GET.get('show_inactive', 'false') == 'true' + + if show_inactive: + user_roles = UserRole.objects.select_related('user', 'role', 'created_by').all() + else: + # По умолчанию показываем только активных + user_roles = UserRole.objects.select_related('user', 'role', 'created_by').filter( + is_active=True, + user__is_active=True + ) + roles = Role.objects.all() context = { 'user_roles': user_roles, 'roles': roles, + 'show_inactive': show_inactive, } return render(request, 'user_roles/user_role_list.html', context) @@ -34,21 +46,52 @@ def user_role_create(request): password = request.POST.get('password', User.objects.make_random_password(12)) try: - # Создаем пользователя - user = User.objects.create_user( - email=email, - name=name, - password=password, - is_email_confirmed=True, - is_staff=False, # SECURITY: Пользователи ролей НЕ имеют доступа к админке - is_superuser=False # SECURITY: Пользователи ролей НЕ суперпользователи - ) + # Проверяем, не существует ли уже пользователь с таким email + existing_user = User.objects.filter(email=email).first() + + if existing_user: + # Пользователь существует - проверяем его статус + if not existing_user.is_active: + # Пользователь деактивирован - предлагаем реактивировать + messages.warning( + request, + f'Пользователь {email} уже существует, но деактивирован. ' + f'Используйте функцию "Реактивировать" в списке пользователей.' + ) + else: + # Пользователь активен - возможно, уже имеет роль в другом тенанте + messages.error( + request, + f'Пользователь с email {email} уже существует в системе.' + ) + # ВАЖНО: Возвращаемся к форме, чтобы пользователь мог исправить email + roles = Role.objects.all() + context = {'roles': roles} + return render(request, 'user_roles/user_role_create.html', context) + else: + # Создаем нового пользователя + user = User.objects.create_user( + email=email, + name=name, + password=password, + is_email_confirmed=True, + is_staff=False, # SECURITY: Пользователи ролей НЕ имеют доступа к админке + is_superuser=False # SECURITY: Пользователи ролей НЕ суперпользователи + ) - # Назначаем роль - RoleService.assign_role_to_user(user, role_code, created_by=request.user) + # Назначаем роль + RoleService.assign_role_to_user(user, role_code, created_by=request.user) - messages.success(request, f'Пользователь {email} создан с ролью {role_code}. Пароль: {password}') - return redirect('user_roles:list') + # DEBUG: Проверяем, что пароль есть + print(f"DEBUG: created_user_email={email}, generated_password={password}") + + # Показываем страницу с паролем + context = { + 'created_user_email': email, + 'generated_password': password, + 'user_role': role_code, + } + return render(request, 'user_roles/user_role_created.html', context) except Exception as e: messages.error(request, f'Ошибка при создании пользователя: {str(e)}') @@ -73,24 +116,55 @@ def user_role_edit(request, pk): return redirect('user_roles:list') if request.method == 'POST': - role_code = request.POST.get('role') - is_active = request.POST.get('is_active') == 'on' + action = request.POST.get('action', 'update') + + # Обработка пересоздания пароля + if action == 'regenerate_password': + try: + new_password = User.objects.make_random_password(12) + user_role.user.set_password(new_password) + user_role.user.save() + + messages.success(request, f'Пароль для пользователя {user_role.user.email} успешно обновлен') + + # Показываем новый пароль в модальном окне + roles = Role.objects.all() + context = { + 'user_role': user_role, + 'roles': roles, + 'generated_password': new_password, + } + return render(request, 'user_roles/user_role_edit.html', context) + + except Exception as e: + messages.error(request, f'Ошибка при обновлении пароля: {str(e)}') + + # Обычное обновление роли и имени + else: + role_code = request.POST.get('role') + is_active = request.POST.get('is_active') == 'on' + name = request.POST.get('name', '').strip() - try: - # Обновляем роль - role = RoleService.get_role_by_code(role_code) - if not role: - raise ValueError(f"Роль '{role_code}' не найдена") + try: + # Обновляем роль + role = RoleService.get_role_by_code(role_code) + if not role: + raise ValueError(f"Роль '{role_code}' не найдена") - user_role.role = role - user_role.is_active = is_active - user_role.save() + user_role.role = role + user_role.is_active = is_active + user_role.save() + + # Обновляем имя пользователя + if name and name != user_role.user.name: + user_role.user.name = name + user_role.user.save() - messages.success(request, f'Роль пользователя {user_role.user.email} обновлена') - return redirect('user_roles:list') + messages.success(request, f'Данные пользователя {user_role.user.email} обновлены') + return redirect('user_roles:list') - except Exception as e: - messages.error(request, f'Ошибка при обновлении роли: {str(e)}') + except Exception as e: + messages.error(request, f'Ошибка при обновлении данных: {str(e)}') roles = Role.objects.all() context = { @@ -103,7 +177,7 @@ def user_role_edit(request, pk): @login_required @owner_required def user_role_delete(request, pk): - """Удаление роли пользователя (отключение доступа)""" + """Деактивация пользователя (soft delete)""" user_role = get_object_or_404(UserRole, pk=pk) # Защита от самоблокировки @@ -114,11 +188,43 @@ def user_role_delete(request, pk): if request.method == 'POST': email = user_role.user.email - user_role.delete() - messages.success(request, f'Доступ пользователя {email} удален') + + # Soft delete: деактивируем пользователя и его роль + user_role.is_active = False + user_role.save() + + user_role.user.is_active = False + user_role.user.save() + + messages.success(request, f'Пользователь {email} деактивирован') return redirect('user_roles:list') context = { 'user_role': user_role, } return render(request, 'user_roles/user_role_delete.html', context) + + +@login_required +@owner_required +def user_role_reactivate(request, pk): + """Реактивация пользователя""" + user_role = get_object_or_404(UserRole, pk=pk) + + if request.method == 'POST': + email = user_role.user.email + + # Реактивируем пользователя и его роль + user_role.is_active = True + user_role.save() + + user_role.user.is_active = True + user_role.user.save() + + messages.success(request, f'Пользователь {email} успешно реактивирован') + return redirect('user_roles:list') + + context = { + 'user_role': user_role, + } + return render(request, 'user_roles/user_role_reactivate.html', context) diff --git a/ГИД ПО ЗАПУСКУ b/ГИД ПО ЗАПУСКУ index e4f0932..ad4aa45 100644 --- a/ГИД ПО ЗАПУСКУ +++ b/ГИД ПО ЗАПУСКУ @@ -42,3 +42,8 @@ client = Client.objects.get(schema_name='public') with schema_context(client): call_command('createsuperuser') exit() + +# 7. Восстановление стандартных ролей для конкретного тенанта +# Если роли были удалены или нужно их пересоздать: +python manage.py tenant_command init_roles --schema=anatol +# Где 'anatol' - имя схемы тенанта (schema_name)