Files
octopus/myproject/accounts/models.py
Andrey Smakotin b562eabcaf refactor(admin): удален избыточный admin_access_middleware
- Удален TenantAdminAccessMiddleware (избыточен — Django Admin уже проверяет is_staff)
- CustomUser.create_superuser теперь устанавливает is_staff=False (нет доступа к /admin/)
- PlatformAdmin с is_staff=True сохраняет доступ к админке
- Обновлен комментарий в onboarding.py

Доступ к /admin/ теперь контролируется стандартным механизмом Django Admin
через has_permission() + существующие authentication backends.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 22:00:14 +03:00

155 lines
6.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
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')