Обновления в myproject: настройки, URL и middleware доступа к админке
This commit is contained in:
@@ -1,23 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Middleware для ограничения доступа к админке Django на поддоменах тенантов.
|
||||
Middleware для ограничения доступа к админке Django.
|
||||
|
||||
SECURITY: Этот middleware обеспечивает дополнительный слой защиты,
|
||||
блокируя доступ владельцев тенантов к /admin/ даже если у них
|
||||
случайно установлен is_staff=True.
|
||||
SECURITY: Этот middleware обеспечивает изоляцию аутентификации между
|
||||
PlatformAdmin (public schema) и CustomUser (tenant schemas).
|
||||
|
||||
Правила доступа:
|
||||
- На tenant поддоменах (shop1.localhost): только is_superuser=True может входить в /admin/
|
||||
- На public схеме (localhost): обычные правила Django (is_staff=True достаточно)
|
||||
- Суперпользователи имеют доступ везде
|
||||
Правила доступа к /admin/:
|
||||
- На PUBLIC домене: только PlatformAdmin может входить
|
||||
- На TENANT доменах: только PlatformAdmin с is_superuser=True (для поддержки)
|
||||
или CustomUser с is_superuser=True (системный пользователь тенанта)
|
||||
"""
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.db import connection
|
||||
|
||||
|
||||
class TenantAdminAccessMiddleware:
|
||||
"""
|
||||
Дополнительный слой безопасности: блокирует доступ владельцев тенантов к /admin/
|
||||
даже если у них случайно установлен is_staff=True.
|
||||
Middleware для контроля доступа к Django Admin.
|
||||
|
||||
Обеспечивает разделение между:
|
||||
- PlatformAdmin (администраторы платформы) на public домене
|
||||
- CustomUser (пользователи тенантов) на tenant доменах
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
@@ -26,20 +29,42 @@ class TenantAdminAccessMiddleware:
|
||||
def __call__(self, request):
|
||||
# Проверяем, это admin URL?
|
||||
if request.path.startswith('/admin/'):
|
||||
from django.db import connection
|
||||
# Импортируем здесь чтобы избежать circular imports
|
||||
from platform_admin.models import PlatformAdmin
|
||||
|
||||
# Получаем текущую schema
|
||||
schema_name = getattr(connection, 'schema_name', 'public')
|
||||
is_public = schema_name == 'public'
|
||||
|
||||
# Если мы в tenant схеме (не public)
|
||||
if hasattr(connection, 'tenant') and connection.tenant:
|
||||
# Проверяем: это не public схема?
|
||||
if connection.tenant.schema_name != 'public':
|
||||
# Проверяем наличие атрибута user (добавляется AuthenticationMiddleware)
|
||||
if hasattr(request, 'user'):
|
||||
# Если пользователь авторизован, но НЕ суперпользователь - блокируем
|
||||
if request.user.is_authenticated and not request.user.is_superuser:
|
||||
if hasattr(request, 'user') and request.user.is_authenticated:
|
||||
user = request.user
|
||||
is_platform_admin = isinstance(user, PlatformAdmin)
|
||||
|
||||
if is_public:
|
||||
# PUBLIC DOMAIN: только PlatformAdmin
|
||||
if not is_platform_admin:
|
||||
return HttpResponseForbidden(
|
||||
"Доступ запрещен. Только системные администраторы могут "
|
||||
"заходить в админ-панель на поддоменах тенантов. "
|
||||
"Используйте панель управления тенанта."
|
||||
"Доступ запрещен. Админка платформы доступна только "
|
||||
"для администраторов платформы. "
|
||||
"Если вы владелец магазина, перейдите на домен вашего магазина."
|
||||
)
|
||||
else:
|
||||
# TENANT DOMAIN: PlatformAdmin (superuser) или CustomUser (superuser)
|
||||
if is_platform_admin:
|
||||
# PlatformAdmin на tenant домене - только суперадмин
|
||||
if not user.is_superuser:
|
||||
return HttpResponseForbidden(
|
||||
"Доступ запрещен. Только суперадминистраторы платформы "
|
||||
"могут входить в админку тенантов."
|
||||
)
|
||||
else:
|
||||
# CustomUser на tenant домене
|
||||
if not user.is_superuser:
|
||||
return HttpResponseForbidden(
|
||||
"Доступ запрещен. Только системные администраторы тенанта "
|
||||
"могут входить в Django Admin. "
|
||||
"Используйте панель управления магазином."
|
||||
)
|
||||
|
||||
response = self.get_response(request)
|
||||
|
||||
@@ -58,6 +58,7 @@ CSRF_USE_SESSIONS = True # Рекомендуется для мультихос
|
||||
SHARED_APPS = [
|
||||
'django_tenants', # ОБЯЗАТЕЛЬНО ПЕРВЫМ!
|
||||
'tenants', # Приложение с моделями тенантов
|
||||
'platform_admin', # Администраторы платформы (отдельная модель от CustomUser)
|
||||
|
||||
# Django встроенные приложения
|
||||
'django.contrib.contenttypes',
|
||||
@@ -67,12 +68,8 @@ SHARED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
# Accounts должен быть в shared для CustomUser (используется в админке)
|
||||
'accounts',
|
||||
|
||||
# Celery results (для сохранения статуса асинхронных задач)
|
||||
'django_celery_results',
|
||||
'simple_history', # Перенесли сюда для корректной инициализации моделей
|
||||
]
|
||||
|
||||
# Tenant apps: создаются в отдельной схеме для каждого тенанта (изолированные данные)
|
||||
@@ -80,6 +77,11 @@ TENANT_APPS = [
|
||||
'django.contrib.contenttypes', # Дублируем для tenant схем
|
||||
'django.contrib.auth', # Дублируем для tenant схем
|
||||
|
||||
# CustomUser - TENANT-ONLY модель!
|
||||
# Каждый тенант имеет свою таблицу accounts_customuser в своей схеме
|
||||
# Один email в разных тенантах = разные пользователи (полная изоляция)
|
||||
'accounts',
|
||||
|
||||
# Приложения с бизнес-логикой (изолированные для каждого магазина)
|
||||
'nested_admin',
|
||||
'django_filters', # Фильтрация данных
|
||||
@@ -90,6 +92,7 @@ TENANT_APPS = [
|
||||
'inventory', # Складской учет
|
||||
'pos', # POS Terminal
|
||||
'system_settings', # Системные настройки компании (только для владельца)
|
||||
# TODO: 'simple_history' - вернуть позже для истории изменений
|
||||
]
|
||||
|
||||
# Объединяем для INSTALLED_APPS
|
||||
@@ -137,11 +140,19 @@ if DEBUG_TOOLBAR_ENABLED:
|
||||
# AUTHENTICATION BACKENDS
|
||||
# ============================================
|
||||
|
||||
# Кастомный backend для связи ролей с Django permissions API
|
||||
# ВАЖНО: Этот backend работает с ролями из tenant schema, НЕ трогая public schema!
|
||||
# Authentication backends для изолированной аутентификации
|
||||
# ВАЖНО: Порядок важен! Django пробует backends по очереди до первого успеха
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
'user_roles.auth_backend.RoleBasedPermissionBackend', # Наш кастомный backend для ролей
|
||||
'django.contrib.auth.backends.ModelBackend', # Стандартный backend (для superuser и т.д.)
|
||||
# 1. PlatformAdmin - администраторы платформы (public schema)
|
||||
# Работает на public домене, а также на tenant доменах для суперадминов (поддержка)
|
||||
'platform_admin.backends.PlatformAdminBackend',
|
||||
|
||||
# 2. CustomUser - пользователи тенантов (tenant schema)
|
||||
# Работает ТОЛЬКО на tenant доменах, ищет пользователя в текущей schema
|
||||
'accounts.backends.TenantUserBackend',
|
||||
|
||||
# 3. Ролевой backend для проверки permissions (работает с RoleService)
|
||||
'user_roles.auth_backend.RoleBasedPermissionBackend',
|
||||
]
|
||||
|
||||
|
||||
@@ -248,6 +259,9 @@ TENANT_DOMAIN_BASE = env('TENANT_DOMAIN_BASE', default='localhost:8000')
|
||||
# Использовать HTTPS для ссылок (в проде True, локально False)
|
||||
USE_HTTPS = env.bool('USE_HTTPS', default=False)
|
||||
|
||||
# Email техподдержки платформы (создаётся скрытый аккаунт в каждом тенанте)
|
||||
PLATFORM_SUPPORT_EMAIL = env('PLATFORM_SUPPORT_EMAIL', default=None)
|
||||
|
||||
|
||||
# ============================================
|
||||
# SESSION CONFIGURATION
|
||||
@@ -491,18 +505,10 @@ DEFAULT_FROM_EMAIL = 'noreply@inventory.by'
|
||||
# AUTHENTICATION
|
||||
# ============================================
|
||||
|
||||
AUTH_USER_MODEL = 'accounts.CustomUser'
|
||||
|
||||
|
||||
# ============================================
|
||||
# TENANT ADMIN AUTO-CREATION
|
||||
# ============================================
|
||||
|
||||
# При создании нового тенанта автоматически создается суперпользователь
|
||||
# с указанными credentials для доступа к админке тенанта
|
||||
TENANT_ADMIN_EMAIL = env('TENANT_ADMIN_EMAIL')
|
||||
TENANT_ADMIN_PASSWORD = env('TENANT_ADMIN_PASSWORD')
|
||||
TENANT_ADMIN_NAME = env('TENANT_ADMIN_NAME')
|
||||
# AUTH_USER_MODEL указывает на PlatformAdmin (public schema)
|
||||
# Это нужно для django.contrib.admin.LogEntry и других Django-компонентов
|
||||
# CustomUser живёт в TENANT_APPS и НЕ является AUTH_USER_MODEL
|
||||
AUTH_USER_MODEL = 'platform_admin.PlatformAdmin'
|
||||
|
||||
|
||||
# ============================================
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
URL configuration for the PUBLIC schema (inventory.by domain).
|
||||
|
||||
This is the main domain where:
|
||||
- Super admin can access admin panel
|
||||
- PlatformAdmin can access admin panel and dashboard
|
||||
- Tenant registration is available
|
||||
- Password setup for new tenant owners
|
||||
|
||||
IMPORTANT: На public домене НЕ работает аутентификация CustomUser!
|
||||
CustomUser живёт в tenant schemas и доступен только на tenant доменах.
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
@@ -14,9 +18,18 @@ from django.urls import re_path
|
||||
from django.views.static import serve
|
||||
|
||||
urlpatterns = [
|
||||
# Django Admin для PlatformAdmin
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('tenants.urls')), # Роуты для регистрации тенантов (/, /register/, /register/success/)
|
||||
path('accounts/', include('accounts.urls')), # Роуты для установки пароля владельцами тенантов
|
||||
|
||||
# Панель управления платформой (login/logout/dashboard для PlatformAdmin)
|
||||
path('platform/', include('platform_admin.urls')),
|
||||
|
||||
# Регистрация тенантов (/, /register/, /register/success/)
|
||||
path('', include('tenants.urls')),
|
||||
|
||||
# Установка пароля владельцами тенантов (только setup-password)
|
||||
# НЕ включаем полный accounts.urls - на public домене нельзя логиниться как CustomUser
|
||||
path('accounts/', include('accounts.urls')),
|
||||
]
|
||||
|
||||
# Serve media files in development
|
||||
|
||||
Reference in New Issue
Block a user