- Добавить display_name в CustomUser (name или email) - Исправить get_showcase_kits_api: заменить username на display_name - Использовать Case/When с output_field для выбора имени на уровне БД Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
160 lines
6.7 KiB
Python
160 lines
6.7 KiB
Python
from django.db import models
|
||
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
|
||
from django.utils import timezone
|
||
import uuid
|
||
|
||
|
||
class CustomUserManager(BaseUserManager):
|
||
def create_user(self, email, name, password=None, **extra_fields):
|
||
if not email:
|
||
raise ValueError('Email обязателен')
|
||
email = self.normalize_email(email)
|
||
|
||
# SECURITY FIX: Явно устанавливаем флаги безопасности в False по умолчанию
|
||
# Обычные пользователи НЕ должны иметь доступ к админке
|
||
extra_fields.setdefault('is_staff', False)
|
||
extra_fields.setdefault('is_superuser', False)
|
||
|
||
user = self.model(email=email, name=name, **extra_fields)
|
||
user.set_password(password)
|
||
user.save(using=self._db)
|
||
return user
|
||
|
||
def create_superuser(self, email, name, password=None, **extra_fields):
|
||
extra_fields.setdefault('is_staff', False) # CustomUser не должен иметь доступ к Django Admin
|
||
extra_fields.setdefault('is_superuser', True)
|
||
extra_fields.setdefault('is_active', True)
|
||
# Суперпользователь автоматически имеет подтвержденный email
|
||
extra_fields.setdefault('is_email_confirmed', True)
|
||
|
||
if extra_fields.get('is_superuser') is not True:
|
||
raise ValueError('Суперпользователь должен иметь is_superuser=True.')
|
||
|
||
user = self.create_user(email, name, password, **extra_fields)
|
||
# Устанавливаем дату подтверждения email
|
||
if user.is_email_confirmed and not user.email_confirmed_at:
|
||
user.email_confirmed_at = timezone.now()
|
||
user.save()
|
||
return user
|
||
|
||
|
||
class CustomUser(AbstractBaseUser):
|
||
"""
|
||
Пользователь тенанта (магазина).
|
||
|
||
ВАЖНО: Эта модель в TENANT_APPS - каждый тенант имеет свою таблицу!
|
||
Один email в разных тенантах = разные записи в разных схемах БД.
|
||
Полная изоляция обеспечивается на уровне PostgreSQL schemas.
|
||
|
||
НЕ является AUTH_USER_MODEL (это PlatformAdmin).
|
||
НЕ использует Django Groups/Permissions - используется своя система ролей (UserRole).
|
||
"""
|
||
email = models.EmailField(unique=True)
|
||
name = models.CharField(max_length=100)
|
||
|
||
# Стандартные поля для совместимости с Django auth
|
||
is_active = models.BooleanField(default=True)
|
||
is_staff = models.BooleanField(default=False) # Для доступа к админке (если нужно)
|
||
is_superuser = models.BooleanField(default=False) # Для полных прав в тенанте
|
||
date_joined = models.DateTimeField(default=timezone.now)
|
||
|
||
is_email_confirmed = models.BooleanField(default=False)
|
||
email_confirmation_token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
||
email_confirmed_at = models.DateTimeField(null=True, blank=True)
|
||
password_reset_token = models.UUIDField(null=True, blank=True, editable=False, unique=True)
|
||
|
||
USERNAME_FIELD = 'email'
|
||
REQUIRED_FIELDS = ['name']
|
||
|
||
objects = CustomUserManager()
|
||
|
||
class Meta:
|
||
verbose_name = "Пользователь магазина"
|
||
verbose_name_plural = "Пользователи магазина"
|
||
|
||
def __str__(self):
|
||
return self.email
|
||
|
||
@property
|
||
def display_name(self):
|
||
"""Отображаемое имя пользователя: имя если есть, иначе email"""
|
||
return self.name or self.email
|
||
|
||
def has_perm(self, perm, obj=None):
|
||
"""
|
||
Проверка разрешения через authentication backends.
|
||
Django вызывает все зарегистрированные backends по очереди.
|
||
"""
|
||
if not self.is_active:
|
||
return False
|
||
|
||
# Импортируем здесь, чтобы избежать циклических импортов
|
||
from django.contrib.auth import get_backends
|
||
|
||
for backend in get_backends():
|
||
if hasattr(backend, 'has_perm'):
|
||
result = backend.has_perm(self, perm, obj)
|
||
if result is not None: # Backend обработал запрос
|
||
return result
|
||
|
||
return False
|
||
|
||
def has_module_perms(self, app_label):
|
||
"""
|
||
Проверка разрешений для модуля через authentication backends.
|
||
"""
|
||
if not self.is_active:
|
||
return False
|
||
|
||
from django.contrib.auth import get_backends
|
||
|
||
for backend in get_backends():
|
||
if hasattr(backend, 'has_module_perms'):
|
||
result = backend.has_module_perms(self, app_label)
|
||
if result is not None: # Backend обработал запрос
|
||
return result
|
||
|
||
return False
|
||
|
||
def generate_confirmation_token(self):
|
||
"""Генерирует новый токен для подтверждения email"""
|
||
self.email_confirmation_token = uuid.uuid4()
|
||
self.save()
|
||
return self.email_confirmation_token
|
||
|
||
def confirm_email(self):
|
||
"""Подтверждает email пользователя"""
|
||
self.is_email_confirmed = True
|
||
self.email_confirmed_at = timezone.now()
|
||
self.save()
|
||
|
||
def get_tenant_role(self):
|
||
"""Получить роль пользователя в текущем тенанте"""
|
||
from user_roles.services import RoleService
|
||
return RoleService.get_user_role(self)
|
||
|
||
def has_role(self, *role_codes):
|
||
"""Проверить, имеет ли пользователь одну из указанных ролей"""
|
||
from user_roles.services import RoleService
|
||
return RoleService.user_has_role(self, *role_codes)
|
||
|
||
@property
|
||
def is_owner(self):
|
||
"""Является ли пользователь владельцем"""
|
||
return self.has_role('owner')
|
||
|
||
@property
|
||
def is_manager(self):
|
||
"""Является ли пользователь менеджером"""
|
||
return self.has_role('manager')
|
||
|
||
@property
|
||
def is_florist(self):
|
||
"""Является ли пользователь флористом"""
|
||
return self.has_role('florist')
|
||
|
||
@property
|
||
def is_courier(self):
|
||
"""Является ли пользователь курьером"""
|
||
return self.has_role('courier')
|