Рефакторинг: убрана зависимость от Django Groups/Permissions для CustomUser
- CustomUser теперь наследуется от AbstractBaseUser (вместо AbstractUser) - Удалены поля groups и user_permissions из CustomUser - Все authentication backends (TenantUserBackend, PlatformAdminBackend, RoleBasedPermissionBackend) больше НЕ наследуются от ModelBackend - Добавлены методы has_perm() и has_module_perms() в CustomUser для делегирования проверки прав кастомным backends - Полная изоляция: CustomUser использует только систему ролей (UserRole), PlatformAdmin использует только is_superuser - Удалён весь старый код, связанный с Django permissions - Нет обратной совместимости (не требуется) - Чистая архитектура для multi-tenant приложения
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser, BaseUserManager
|
||||
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
|
||||
from django.utils import timezone
|
||||
import uuid
|
||||
|
||||
@@ -9,15 +9,13 @@ class CustomUserManager(BaseUserManager):
|
||||
if not email:
|
||||
raise ValueError('Email обязателен')
|
||||
email = self.normalize_email(email)
|
||||
# Generate a unique username based on email to satisfy the AbstractUser constraint
|
||||
username = email
|
||||
|
||||
# SECURITY FIX: Явно устанавливаем флаги безопасности в False по умолчанию
|
||||
# Обычные пользователи НЕ должны иметь доступ к админке
|
||||
extra_fields.setdefault('is_staff', False)
|
||||
extra_fields.setdefault('is_superuser', False)
|
||||
|
||||
user = self.model(email=email, name=name, username=username, **extra_fields)
|
||||
user = self.model(email=email, name=name, **extra_fields)
|
||||
user.set_password(password)
|
||||
user.save(using=self._db)
|
||||
return user
|
||||
@@ -42,7 +40,7 @@ class CustomUserManager(BaseUserManager):
|
||||
return user
|
||||
|
||||
|
||||
class CustomUser(AbstractUser):
|
||||
class CustomUser(AbstractBaseUser):
|
||||
"""
|
||||
Пользователь тенанта (магазина).
|
||||
|
||||
@@ -51,9 +49,16 @@ class CustomUser(AbstractUser):
|
||||
Полная изоляция обеспечивается на уровне 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)
|
||||
@@ -65,22 +70,6 @@ class CustomUser(AbstractUser):
|
||||
|
||||
objects = CustomUserManager()
|
||||
|
||||
# Изменяем related_name для избежания конфликта с встроенной моделью User
|
||||
groups = models.ManyToManyField(
|
||||
'auth.Group',
|
||||
related_name='custom_user_set',
|
||||
blank=True,
|
||||
verbose_name='groups',
|
||||
help_text='The groups this user belongs to.',
|
||||
)
|
||||
user_permissions = models.ManyToManyField(
|
||||
'auth.Permission',
|
||||
related_name='custom_user_set',
|
||||
blank=True,
|
||||
verbose_name='user permissions',
|
||||
help_text='Specific permissions for this user.',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Пользователь магазина"
|
||||
verbose_name_plural = "Пользователи магазина"
|
||||
@@ -88,6 +77,42 @@ class CustomUser(AbstractUser):
|
||||
def __str__(self):
|
||||
return 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()
|
||||
|
||||
Reference in New Issue
Block a user