b788bc2a7c
- EncryptedCharField (core/models/fields.py): прозрачное Fernet-шифрование на границе с БД, читатели и админка не меняются - ConnectorConfig.password/token переведены на EncryptedCharField, колонки расширены под шифртекст - Миграция 0004: расширение колонок -> шифрование существующих значений -> смена типа - FIELD_ENCRYPTION_KEY в settings (default = SECRET_KEY); на проде задавать отдельно ДО migrate Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
71 lines
3.2 KiB
Python
71 lines
3.2 KiB
Python
from django.db import models
|
||
from django.utils.translation import gettext_lazy as _
|
||
from django.core.exceptions import ValidationError
|
||
from .base import BaseModel
|
||
from .fields import EncryptedCharField
|
||
|
||
|
||
class ConnectorConfig(BaseModel):
|
||
CONNECTOR_TYPES = [
|
||
('mattermost', 'Mattermost'),
|
||
('ad', 'Active Directory'),
|
||
('carbonio', 'Carbonio CE'),
|
||
('mail', 'Почта (IMAP/SMTP)'),
|
||
]
|
||
|
||
connector_type = models.CharField(
|
||
_('Тип коннектора'), max_length=20,
|
||
choices=CONNECTOR_TYPES, unique=True
|
||
)
|
||
is_enabled = models.BooleanField(_('Включён'), default=False)
|
||
|
||
# Подключение
|
||
host = models.CharField(_('Хост'), max_length=255, blank=True)
|
||
port = models.PositiveIntegerField(_('Порт'), null=True, blank=True)
|
||
use_ssl = models.BooleanField(_('Использовать SSL'), default=True)
|
||
|
||
# Аутентификация
|
||
username = models.CharField(_('Логин'), max_length=255, blank=True)
|
||
# password/token хранятся в БД зашифрованными (Fernet). max_length задан с
|
||
# запасом под шифртекст, который длиннее открытого текста.
|
||
password = EncryptedCharField(_('Пароль'), max_length=512, blank=True)
|
||
token = EncryptedCharField(
|
||
_('Токен / API Key'), max_length=1024, blank=True,
|
||
help_text=_('Bearer-токен или API-ключ, если используется вместо логина/пароля')
|
||
)
|
||
|
||
# Дополнительные параметры коннектора
|
||
extra_config = models.JSONField(
|
||
_('Дополнительные параметры'), default=dict, blank=True,
|
||
help_text=_('Специфичные для коннектора настройки в формате JSON')
|
||
)
|
||
|
||
notes = models.TextField(_('Примечания'), blank=True)
|
||
|
||
class Meta:
|
||
verbose_name = _('Настройки коннектора')
|
||
verbose_name_plural = _('Настройки коннекторов')
|
||
ordering = ['connector_type']
|
||
|
||
def __str__(self):
|
||
status = '✓' if self.is_enabled else '✗'
|
||
return f'{status} {self.get_connector_type_display()}'
|
||
|
||
def clean(self):
|
||
super().clean()
|
||
if self.is_enabled and not self.host:
|
||
raise ValidationError({'host': _('Хост обязателен для включённого коннектора')})
|
||
if self.is_enabled and not self.token and not self.username:
|
||
raise ValidationError(
|
||
_('Для включённого коннектора необходимо указать токен или логин')
|
||
)
|
||
|
||
@classmethod
|
||
def get_for(cls, connector_type: str) -> 'ConnectorConfig | None':
|
||
"""Получить конфиг коннектора. Возвращает None если не найден или отключён."""
|
||
try:
|
||
cfg = cls.objects.get(connector_type=connector_type)
|
||
return cfg if cfg.is_enabled else None
|
||
except cls.DoesNotExist:
|
||
return None
|