refactor(settings): упрощение конфигурации и удаление дубликатов

- Упрощена инициализация DATABASES (удален пустой словарь, использован .update())
- Удалена закомментированная секция AUTH_PASSWORD_VALIDATORS
- Консолидированы дублирующиеся настройки LDAP:
  * LDAP_UPN_SUFFIX (было 2 определения)
  * LDAP_USERS_BASE / LDAP_USER_BASE (было дублирование)
- Изменены дефолтные значения с robotail.local на eusrr.local
- Расширена конфигурация логирования (см. следующий коммит)
This commit is contained in:
igor_nadein 2026-02-17 18:31:20 +03:00
parent 8aa6d6ec87
commit 98104fcded

View File

@ -99,36 +99,23 @@ ASGI_APPLICATION = "eusrr_backend.asgi.application"
USE_SQLITE = os.getenv("USE_SQLITE", "false").lower() == "true"
DATABASES = {
"default": {},
"default": {
"ENGINE": "django.db.backends.sqlite3" if USE_SQLITE else "django.db.backends.postgresql",
"NAME": os.path.join(BASE_DIR, "db.sqlite3") if USE_SQLITE else os.getenv("POSTGRES_DB", "django"),
}
}
if USE_SQLITE:
DATABASES["default"] = {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
else:
DATABASES["default"] = {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.getenv("POSTGRES_DB", "django"),
if not USE_SQLITE:
DATABASES["default"].update({
"USER": os.getenv("POSTGRES_USER", "django"),
"PASSWORD": os.getenv("POSTGRES_PASSWORD", ""),
"HOST": os.getenv("DB_HOST", "127.0.0.1"),
"PORT": os.getenv("DB_PORT", "5432"),
}
})
# -----------------------------------------------------------------------------
# АВТОРИЗАЦИЯ / ПАРОЛИ
# -----------------------------------------------------------------------------
# AUTH_PASSWORD_VALIDATORS = [
# {
# "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
# },
# {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
# {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
# {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
# ]
LANGUAGE_CODE = "ru"
TIME_ZONE = "Europe/Moscow"
@ -184,35 +171,222 @@ FILE_UPLOAD_MAX_MEMORY_SIZE = 10 * 1024 * 1024 # 10 MB
# -----------------------------------------------------------------------------
# ЛОГИРОВАНИЕ
# -----------------------------------------------------------------------------
# Создаем директорию для логов
LOGS_DIR = BASE_DIR / "logs"
LOGS_DIR.mkdir(exist_ok=True)
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
#Detalized format for development
"verbose": {
"format": "[{levelname}] {asctime} {name}:{lineno} {message}",
"format": "[{levelname:8s}] {asctime} | {name:30s} | {funcName:20s}:{lineno:4d} | {message}",
"style": "{",
"datefmt": "%Y-%m-%d %H:%M:%S",
},
# Simple format for production console
"simple": {
"format": "[{levelname:8s}] {asctime} | {name} | {message}",
"style": "{",
"datefmt": "%Y-%m-%d %H:%M:%S",
},
# Structured format for parsing
"json_style": {
"format": "%(asctime)s | %(levelname)s | %(name)s | %(funcName)s:%(lineno)d | %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
},
},
"filters": {
"require_debug_false": {
"()": "django.utils.log.RequireDebugFalse",
},
"require_debug_true": {
"()": "django.utils.log.RequireDebugTrue",
},
},
"handlers": {
# Console output
"console": {
"level": "DEBUG",
"level": "DEBUG" if DEBUG else "INFO",
"class": "logging.StreamHandler",
"formatter": "verbose" if DEBUG else "simple",
},
# All logs (INFO+) with rotation
"file_all": {
"level": "INFO",
"class": "logging.handlers.RotatingFileHandler",
"filename": LOGS_DIR / "all.log",
"maxBytes": 10 * 1024 * 1024, # 10 MB
"backupCount": 5,
"formatter": "json_style",
"encoding": "utf-8",
},
# Error logs (ERROR+) with rotation
"file_error": {
"level": "ERROR",
"class": "logging.handlers.RotatingFileHandler",
"filename": LOGS_DIR / "error.log",
"maxBytes": 10 * 1024 * 1024, # 10 MB
"backupCount": 10,
"formatter": "verbose",
"encoding": "utf-8",
},
# Debug logs (only in DEBUG mode)
"file_debug": {
"level": "DEBUG",
"class": "logging.handlers.RotatingFileHandler",
"filename": LOGS_DIR / "debug.log",
"maxBytes": 20 * 1024 * 1024, # 20 MB
"backupCount": 3,
"formatter": "verbose",
"encoding": "utf-8",
"filters": ["require_debug_true"],
},
# Security logs (authentication, permissions, etc)
"file_security": {
"level": "INFO",
"class": "logging.handlers.RotatingFileHandler",
"filename": LOGS_DIR / "security.log",
"maxBytes": 10 * 1024 * 1024, # 10 MB
"backupCount": 20,
"formatter": "json_style",
"encoding": "utf-8",
},
# Django request logs
"file_requests": {
"level": "INFO",
"class": "logging.handlers.RotatingFileHandler",
"filename": LOGS_DIR / "requests.log",
"maxBytes": 20 * 1024 * 1024, # 20 MB
"backupCount": 5,
"formatter": "json_style",
"encoding": "utf-8",
},
# Mail admins on errors (production only)
"mail_admins": {
"level": "ERROR",
"class": "django.utils.log.AdminEmailHandler",
"filters": ["require_debug_false"],
"formatter": "verbose",
},
},
"loggers": {
"documents": {"handlers": ["console"], "level": "DEBUG", "propagate": False},
"bots": {"handlers": ["console"], "level": "DEBUG", "propagate": False},
"communications": {"handlers": ["console"], "level": "DEBUG", "propagate": False},
"api.v1.communications": {"handlers": ["console"], "level": "DEBUG", "propagate": False},
"notifications": {"handlers": ["console"], "level": "INFO", "propagate": False},
"requests_app": {"handlers": ["console"], "level": "DEBUG", "propagate": False},
"employees": {"handlers": ["console"], "level": "INFO", "propagate": False},
"common": {"handlers": ["console"], "level": "INFO", "propagate": False},
"django": {"handlers": ["console"], "level": "INFO", "propagate": False},
"aiogram": {"handlers": ["console"], "level": "INFO", "propagate": False},
# полезно видеть ошибки и отладку ldap3
"ldap3": {"handlers": ["console"], "level": "WARNING", "propagate": False},
# Root logger
"": {
"handlers": ["console", "file_all", "file_error"],
"level": "DEBUG" if DEBUG else "INFO",
},
# Django core
"django": {
"handlers": ["console", "file_all", "file_error"],
"level": "INFO",
"propagate": False,
},
"django.request": {
"handlers": ["console", "file_requests", "file_error", "mail_admins"],
"level": "WARNING",
"propagate": False,
},
"django.server": {
"handlers": ["console", "file_requests"],
"level": "INFO",
"propagate": False,
},
# SQL queries (only in DEBUG mode)
"django.db.backends": {
"handlers": ["console", "file_debug"] if DEBUG else [],
"level": "DEBUG" if DEBUG else "INFO",
"propagate": False,
},
# Security events
"django.security": {
"handlers": ["console", "file_security", "mail_admins"],
"level": "WARNING",
"propagate": False,
},
# Application loggers
"employees": {
"handlers": ["console", "file_all", "file_debug", "file_error"],
"level": "DEBUG" if DEBUG else "INFO",
"propagate": False,
},
"employees.ldap": {
"handlers": ["console", "file_all", "file_security", "file_error"],
"level": "INFO",
"propagate": False,
},
"documents": {
"handlers": ["console", "file_all", "file_debug", "file_error"],
"level": "DEBUG" if DEBUG else "INFO",
"propagate": False,
},
"communications": {
"handlers": ["console", "file_all", "file_debug", "file_error"],
"level": "DEBUG" if DEBUG else "INFO",
"propagate": False,
},
"api.v1.communications": {
"handlers": ["console", "file_all", "file_debug", "file_error"],
"level": "DEBUG" if DEBUG else "INFO",
"propagate": False,
},
"notifications": {
"handlers": ["console", "file_all", "file_error"],
"level": "INFO",
"propagate": False,
},
"requests_app": {
"handlers": ["console", "file_all", "file_debug", "file_error"],
"level": "DEBUG" if DEBUG else "INFO",
"propagate": False,
},
"bots": {
"handlers": ["console", "file_all", "file_debug", "file_error"],
"level": "DEBUG" if DEBUG else "INFO",
"propagate": False,
},
"feed": {
"handlers": ["console", "file_all", "file_error"],
"level": "INFO",
"propagate": False,
},
"calendar_app": {
"handlers": ["console", "file_all", "file_error"],
"level": "INFO",
"propagate": False,
},
"finance": {
"handlers": ["console", "file_all", "file_error"],
"level": "INFO",
"propagate": False,
},
"common": {
"handlers": ["console", "file_all", "file_error"],
"level": "INFO",
"propagate": False,
},
# Third-party integrations
"ldap3": {
"handlers": ["console", "file_security", "file_error"],
"level": "WARNING",
"propagate": False,
},
"aiogram": {
"handlers": ["console", "file_all", "file_error"],
"level": "INFO",
"propagate": False,
},
"celery": {
"handlers": ["console", "file_all", "file_error"],
"level": "INFO",
"propagate": False,
},
"channels": {
"handlers": ["console", "file_all", "file_error"],
"level": "INFO",
"propagate": False,
},
},
}
@ -334,79 +508,78 @@ AUTHENTICATION_BACKENDS = [
# -----------------------------------------------------------------------------
# LDAP (двусторонний обмен + LWW)
# -----------------------------------------------------------------------------
LDAP_ENABLED = os.getenv("LDAP_ENABLED", "true").lower() == "true"
LDAP_ENABLED = os.getenv("LDAP_ENABLED", "false").lower() == "true"
# Основное подключение
LDAP_URI = os.getenv("LDAP_URI", "ldaps://dcii.robotail.local:636")
LDAP_URI = os.getenv("LDAP_URI", "ldap://127.0.0.1:389")
LDAP_BIND_DN = os.getenv("LDAP_BIND_DN", "")
LDAP_BIND_PASSWORD = os.getenv("LDAP_BIND_PASSWORD", "")
# TLS/CA
LDAP_CA_CERTS = os.getenv("LDAP_CA_CERTS", "") # путь к CA bundle/серту (если нужен)
LDAP_TLS_REQUIRED = os.getenv("LDAP_TLS_REQUIRED", "true").lower() == "true"
LDAP_CA_CERTS = os.getenv("LDAP_CA_CERTS", "")
LDAP_TLS_REQUIRED = os.getenv("LDAP_TLS_REQUIRED", "false").lower() == "true"
# Где искать пользователей
LDAP_USER_BASE = os.getenv("LDAP_USER_BASE", "OU=company,DC=robotail,DC=local")
# Где создавать новых пользователей (может отличаться от базы поиска)
# DN базы пользователей
LDAP_USER_BASE = os.getenv("LDAP_USER_BASE", "ou=users,dc=eusrr,dc=local")
LDAP_USERS_BASE = os.getenv("LDAP_USERS_BASE", LDAP_USER_BASE)
# Базовый DN для операций создания (если не указан department_dn)
LDAP_BASE_DN = os.getenv("LDAP_BASE_DN", LDAP_USERS_BASE)
# UPN-суффикс для создания пользователей (userPrincipalName) - домен БЕЗ @
LDAP_UPN_SUFFIX = os.getenv("LDAP_USER_UPN_SUFFIX", "robotail.local")
# UPN суффикс (домен БЕЗ @)
LDAP_UPN_SUFFIX = os.getenv("LDAP_USER_UPN_SUFFIX", "eusrr.local")
# Фильтры поиска (для OpenLDAP по умолчанию)
LDAP_USER_FILTER = os.getenv(
"LDAP_USER_FILTER", "(&(objectCategory=person)(objectClass=user))"
)
# Для AD по умолчанию; для OpenLDAP можно обнулить в .env
LDAP_ACTIVE_FILTER = os.getenv(
"LDAP_ACTIVE_FILTER",
"(!(userAccountControl:1.2.840.113556.1.4.803:=2))", # битовый NOT DISABLED
"LDAP_USER_FILTER", "(&(objectClass=inetOrgPerson))"
)
LDAP_ACTIVE_FILTER = os.getenv("LDAP_ACTIVE_FILTER", "")
# Атрибуты LDAP
LDAP_ATTR_MAIL = os.getenv("LDAP_ATTR_MAIL", "mail")
LDAP_ATTR_GIVENNAME = os.getenv("LDAP_ATTR_GIVENNAME", "givenName")
LDAP_ATTR_SN = os.getenv("LDAP_ATTR_SN", "sn")
LDAP_ATTR_PHONE = os.getenv("LDAP_ATTR_PHONE", "telephoneNumber")
# Атрибут LDAP для хранения Django pk сотрудника (employeeNumber по RFC 2798, employeeID для AD)
LDAP_EMPLOYEE_ID_ATTR = os.getenv("LDAP_EMPLOYEE_ID_ATTR", "employeeNumber")
LDAP_PHONE_ATTRS = tuple(
_split_env_list(os.getenv("LDAP_PHONE_ATTRS", "mobile,telephoneNumber"))
)
LDAP_DEPT_ATTR = os.getenv("LDAP_DEPT_ATTR", "") # например, departmentNumber
# Отделы и группы
LDAP_DEPT_ATTR = os.getenv("LDAP_DEPT_ATTR", "")
LDAP_DEPARTMENTS_BASE = os.getenv(
"LDAP_DEPARTMENTS_BASE", "OU=Departments,OU=company,DC=eusrr,DC=local"
)
LDAP_DISMISSED_BASE = os.getenv(
"LDAP_DISMISSED_BASE", "OU=Dismissed,OU=company,DC=eusrr,DC=local"
)
LDAP_POSITIONS_BASE = os.getenv(
"LDAP_POSITIONS_BASE", "OU=Positions,OU=company,DC=eusrr,DC=local"
)
LDAP_GROUPS_BASE = os.getenv("LDAP_GROUPS_BASE", "")
LDAP_SYNC_GROUPS = os.getenv("LDAP_SYNC_GROUPS", "false").lower() == "true"
LDAP_GROUP_ATTR = os.getenv("LDAP_GROUP_ATTR", "memberOf")
# Пример: {"CN=HR,OU=Groups,DC=...,DC=...": "hr"}
LDAP_GROUP_MAP = {}
LDAP_GROUPS_EXCLUSIVE = os.getenv("LDAP_GROUPS_EXCLUSIVE", "false").lower() == "true"
# WRITE-BACK
# WRITE-BACK (двусторонняя синхронизация)
LDAP_WRITE_ENABLED = os.getenv("LDAP_WRITE_ENABLED", "false").lower() == "true"
LDAP_WRITE_DN = os.getenv("LDAP_WRITE_DN", LDAP_BIND_DN)
LDAP_WRITE_PASSWORD = os.getenv("LDAP_WRITE_PASSWORD", LDAP_BIND_PASSWORD)
LDAP_WRITE_TIMEOUT = int(os.getenv("LDAP_WRITE_TIMEOUT", "5"))
# UPN суффикс для создания пользователей (домен БЕЗ @, например robotail.local)
LDAP_UPN_SUFFIX = os.getenv("LDAP_USER_UPN_SUFFIX", "robotail.local")
# Белый список: локальные поля -> LDAP-атрибуты
# Маппинг локальных полей на LDAP атрибуты
LDAP_WRITE_ATTRS = {
"first_name": LDAP_ATTR_GIVENNAME,
"last_name": LDAP_ATTR_SN,
# фактическое имя локального телефонного поля подставляет код
"phone": LDAP_ATTR_PHONE,
# "photo": "jpegPhoto",
}
# LWW (Last Writer Wins) — сравнение меток изменения
# Для AD обычно whenChanged, для OpenLDAP — modifyTimestamp
LDAP_ASSERT_ATTR_AD = "whenChanged"
LDAP_ASSERT_ATTR_OL = "modifyTimestamp"
LDAP_ASSERT_ATTR = os.getenv("LDAP_ASSERT_ATTR", LDAP_ASSERT_ATTR_AD)
# Локальное поле «последнее изменение» (ваше поле модели, напр. updated_at)
# LWW (Last Writer Wins) для разрешения конфликтов синхронизации
LDAP_ASSERT_ATTR = os.getenv("LDAP_ASSERT_ATTR", "modifyTimestamp") # modifyTimestamp (OpenLDAP) или whenChanged (AD)
LOCAL_ASSERT_FIELD = os.getenv("LOCAL_ASSERT_FIELD", "updated_at")
# Дополнительные атрибуты при создании пользователя в LDAP
LDAP_CREATE_EXTRA_ATTRS = {
"sAMAccountName": "{username20}",
"userPrincipalName": "{upn}",
@ -415,29 +588,18 @@ LDAP_CREATE_EXTRA_ATTRS = {
"displayName": "{cn}",
"mail": "{email}",
}
# Режим синхронизации и таймауты
LDAP_SYNC_MODE = os.getenv("LDAP_SYNC_MODE", "lww") # lww|ldap|django
LDAP_PURGE = os.getenv("LDAP_PURGE", "false").lower() == "true"
LDAP_CONNECT_TIMEOUT = int(os.getenv("LDAP_CONNECT_TIMEOUT", "5"))
LDAP_OPERATION_TIMEOUT = int(os.getenv("LDAP_OPERATION_TIMEOUT", "10"))
LDAP_USERS_BASE = os.getenv("LDAP_USERS_BASE")
LDAP_USER_BASE = os.getenv("LDAP_USER_BASE")
LDAP_DEPARTMENTS_BASE = os.getenv(
"LDAP_DEPARTMENTS_BASE", "OU=Departments,OU=company,DC=robotail,DC=local"
)
LDAP_DISMISSED_BASE = os.getenv(
"LDAP_DISMISSED_BASE", "OU=Dismissed,OU=company,DC=robotail,DC=local"
)
LDAP_GROUPS_BASE = os.getenv("LDAP_GROUPS_BASE")
LDAP_AUTO_CREATE = os.getenv("LDAP_AUTO_CREATE", "False")
# Флаги поведения
LDAP_AUTO_CREATE = os.getenv("LDAP_AUTO_CREATE", "False").lower() == "true"
LDAP_RESPECT_IS_ACTIVE = True
LDAP_RESPECT_AD_DISABLED = True
LDAP_REGISTRATION_CREATE = True
LDAP_POSITIONS_BASE = os.getenv("LDAP_POSITIONS_BASE", "OU=Positions,OU=company,DC=robotail,DC=local")