feat: переделка под ОКИИ (187-ФЗ) — объекты КИИ вместо ИС, категории 1-3, ОКВЭД, убраны ПДн/бумажные носители
- вкладка «Информационные системы» → «Объекты КИИ», убрана «Структура объекта» - «Тип угроз»/«УЗ» → категория значимости 1/2/3/без категории - удалены Категории ПДн, кол-во записей, перечень/субъекты/действия с ПДн + словари - «Пользователи ИС» → «Пользователи ОКИИ», средства защиты оставлены - вкладка «Бумажные документы» удалена полностью (GUI + логика + модели) - ОКВЭД: поля во вкладке «Организация», словарь okved.json, выбор галочками, импорт - нормализация регистра адреса DaData (КАПС → нормальный) - importer: объекты КИИ (наименование объекта, ПО, СЗИ, пользователи)
This commit is contained in:
+30
-91
@@ -57,7 +57,8 @@ VARIABLE_DEFS = [
|
||||
('{{company_ogrn_date}}', 'A', 'Дата ОГРН', 'company:ogrn_date'),
|
||||
('{{company_kpp}}', 'A', 'КПП', 'company:kpp'),
|
||||
('{{security_department}}', 'A', 'Наименование структурного подразделения по безопасности', 'company:security_department'),
|
||||
('{{item.pd_actions}}', 'B', 'Действия с ПДн (в цикле ИС)', 'item:pd_actions'),
|
||||
('{{company_okved}}', 'A', 'Основной ОКВЭД', 'company:okved'),
|
||||
('{{company_okved_extra}}', 'A', 'Дополнительные ОКВЭД', 'company:okved_extra'),
|
||||
|
||||
# === Комиссия ===
|
||||
('{{commission_list}}', 'A', 'Список комиссии', 'commission_list'),
|
||||
@@ -69,22 +70,15 @@ VARIABLE_DEFS = [
|
||||
('{{document_number}}', 'A', 'Номер документа', 'doc_number'),
|
||||
|
||||
# === 187-ФЗ ===
|
||||
('{{is_security_requirements}}', 'B', 'Требования уровня защищённости', 'security_reqs'),
|
||||
('{{is_list}}', 'B', 'Список всех ИС (нумерованный)', 'is_list'),
|
||||
('{{is_name}}', 'B', 'Наименование первой ИС', 'is:name'),
|
||||
('{{name}}', 'B', 'Название ИС (алиас)', 'is:name'),
|
||||
('{{description}}', 'B', 'Описание ИС (алиас)', 'is:description'),
|
||||
('{{category}}', 'B', 'Категория ИС (алиас)', 'is:category'),
|
||||
('{{is_security_requirements}}', 'B', 'Требования к защите объекта КИИ', 'security_reqs'),
|
||||
('{{is_list}}', 'B', 'Список всех объектов КИИ (нумерованный)', 'is_list'),
|
||||
('{{is_name}}', 'B', 'Наименование первого объекта КИИ', 'is:name'),
|
||||
('{{name}}', 'B', 'Название объекта КИИ (алиас)', 'is:name'),
|
||||
('{{description}}', 'B', 'Описание объекта КИИ (алиас)', 'is:description'),
|
||||
('{{category}}', 'B', 'Категория значимости объекта КИИ (алиас)', 'is:category'),
|
||||
('{{number}}', 'B', 'Номер (алиас)', 'doc_number'),
|
||||
('{{pd_count}}', 'B', 'Количество записей ПДн (алиас)', 'is:pd_count'),
|
||||
('{{pd_list}}', 'B', 'Список ПДн (алиас)', 'is:pd_list'),
|
||||
('{{pd_subjects}}', 'B', 'Субъекты ПДн (алиас)', 'is:pd_subjects'),
|
||||
('{{threat_type}}', 'B', 'Тип угроз (алиас)', 'is:threat_type'),
|
||||
('{{users}}', 'B', 'Пользователи ИС (алиас)', 'is:users'),
|
||||
('{{defence_level}}', 'B', 'Уровень защищённости (алиас)', 'is:defence_level'),
|
||||
('{{users}}', 'B', 'Пользователи ОКИИ (алиас)', 'is:users'),
|
||||
('{{full_name_genitive}}', 'A', 'Полное наименование (род.п., алиас)', 'decl:full_name:gent'),
|
||||
('{{document}}', 'A', 'Название бумажного документа (алиас)', 'company:paper_documents'),
|
||||
('{{storage}}', 'A', 'Место хранения (алиас)', 'company:paper_storage'),
|
||||
('{{responsible_fio_accs}}', 'A', 'ФИО ответственного (вин.п.)', 'fio_acl:responsible'),
|
||||
('{{commission1}}', 'A', 'Должность члена комиссии 1', 'commission_member:0:position'),
|
||||
('{{commission1_fio}}', 'A', 'ФИО члена комиссии 1', 'commission_member:0:fio'),
|
||||
@@ -221,13 +215,11 @@ def _add_is_indexed_vars(replacements: Dict[str, str], isys, idx: int):
|
||||
'is_address': getattr(isys, 'address', '') or '—',
|
||||
'is_description': isys.description or '—',
|
||||
'is_has_internet': 'да' if isys.is_internet else 'нет',
|
||||
'is_defence_level': str(isys.defence_level or '—'),
|
||||
'is_category': ', '.join(isys.personal_data_category) or isys.category or '—',
|
||||
'is_pd_list': ', '.join(isys.personal_data_list or []) or '—',
|
||||
'is_category': isys.category or '—',
|
||||
'is_users': ', '.join(isys.users_list or []) or '—',
|
||||
'is_pd_count': str(isys.pd_count or '—'),
|
||||
'is_purpose': isys.purpose or '—',
|
||||
'is_structure': getattr(isys, 'structure', '') or '—',
|
||||
'is_software': getattr(isys, 'software', '') or '—',
|
||||
}
|
||||
for f, v in fields.items():
|
||||
replacements[f'{{{{{f}_{idx}}}}}'] = str(v)
|
||||
@@ -295,68 +287,16 @@ def _build_loop_data(company: Company, is_list: List) -> Dict[str, List[Dict]]:
|
||||
'address': getattr(isys, 'address', '') or '—',
|
||||
'is_has_lan': 'Да' if isys.is_local_network else 'Нет',
|
||||
'has_internet': 'да' if isys.is_internet else 'нет',
|
||||
'defence_level': str(isys.defence_level or '—'),
|
||||
'threat_type': str(isys.threat_type or '3'),
|
||||
'category': ', '.join(isys.personal_data_category) or isys.category or '—',
|
||||
'pd_list': ', '.join(isys.personal_data_list or []) or '—',
|
||||
'pd_count': str(isys.pd_count or '—'),
|
||||
'category': isys.category or '—',
|
||||
'users': ', '.join(isys.users_list or []) or '—',
|
||||
'users_loop': users_loop,
|
||||
'defense_tools_list': ', '.join(isys.defense_tools_list or []) or '—',
|
||||
'processing_modes': isys.processing_modes or '—',
|
||||
'pd_subjects': '; '.join(isys.pd_subjects_list or []) or '—',
|
||||
'is_purpose': isys.purpose or '—',
|
||||
'structure': getattr(isys, 'structure', '') or '—',
|
||||
'software': getattr(isys, 'software', '') or '—',
|
||||
'pd_actions': '; '.join(isys.pd_actions_list or []) or '—',
|
||||
})
|
||||
loops['information_systems'] = items
|
||||
|
||||
# Бумажные документы
|
||||
def _storage_with_prep(storage: str) -> str:
|
||||
"""«сейф» → «в сейфе» (предлог + предложный падеж).
|
||||
Если уже начинается с предлога — оставить как есть."""
|
||||
if not storage:
|
||||
return ''
|
||||
s = storage.strip()
|
||||
low = s.lower()
|
||||
if low.startswith(('в ', 'на ', 'под ', 'за ', 'у ', 'при ', 'из ', 'со ', 'во ', 'над ', 'перед ')):
|
||||
return s
|
||||
words = s.split()
|
||||
out = []
|
||||
# Исключения предложного падежа: «в шкафу», «в углу» (не «в шкафе»)
|
||||
loct_exceptions = {'шкаф': 'шкафу', 'угол': 'углу', 'край': 'краю', 'рот': 'рту', 'мост': 'мосту'}
|
||||
for w in words:
|
||||
core = w.strip('.,;')
|
||||
if not core or core.isdigit() or core.startswith('№'):
|
||||
out.append(w)
|
||||
continue
|
||||
if core.lower() in loct_exceptions:
|
||||
out.append(loct_exceptions[core.lower()])
|
||||
continue
|
||||
try:
|
||||
p = dc.morph.parse(core)[0]
|
||||
inf = p.inflect({'loct'})
|
||||
if inf:
|
||||
out.append(inf.word)
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
out.append(w)
|
||||
return 'в ' + ' '.join(out)
|
||||
|
||||
paper_list = getattr(company, 'paper_documents_list', None) or []
|
||||
if paper_list:
|
||||
items = []
|
||||
for p in paper_list:
|
||||
storage = p.storage or ''
|
||||
items.append({
|
||||
'document': p.name or '',
|
||||
'storage': _storage_with_prep(storage), # «в сейфе»
|
||||
'storage_raw': storage, # «сейф» (как введено)
|
||||
})
|
||||
loops['paper_documents'] = items
|
||||
|
||||
# Сотрудники с доступом
|
||||
employees = getattr(company, 'employees_access', None) or []
|
||||
if employees:
|
||||
@@ -448,11 +388,6 @@ def _resolve(source: str, ctx: Dict) -> str:
|
||||
'name': 'name',
|
||||
'description': 'description',
|
||||
'category': 'category',
|
||||
'defence_level': 'defence_level',
|
||||
'threat_type': 'threat_type',
|
||||
'pd_list': lambda: ', '.join(first.personal_data_list or []),
|
||||
'pd_count': 'pd_count',
|
||||
'pd_subjects': lambda: '; '.join(first.pd_subjects_list or []),
|
||||
'users': lambda: ', '.join(first.users_list or []),
|
||||
}
|
||||
if field in field_map:
|
||||
@@ -501,21 +436,25 @@ def _get_fio(company: Company, who: str) -> str:
|
||||
|
||||
|
||||
def _get_security_text(ctx: Dict) -> str:
|
||||
"""Возвращает текст требований для уровня защищённости."""
|
||||
defence_level = ctx.get('extended_fields', {}).get('defence_level', '4')
|
||||
"""Возвращает текст требований к защите объекта КИИ по категории значимости."""
|
||||
is_list = ctx.get('is_list', [])
|
||||
if not defence_level and is_list:
|
||||
defence_level = str(getattr(is_list[0], 'defence_level', '4') or '4')
|
||||
dl = str(defence_level).strip()[0]
|
||||
category = ''
|
||||
if is_list:
|
||||
category = str(getattr(is_list[0], 'category', '') or '').strip()
|
||||
|
||||
TEXTS = {
|
||||
'1': """Для обеспечения 1-го уровня защищённости...""",
|
||||
'2': """Для обеспечения 2-го уровня защищённости...""",
|
||||
'3': """Для обеспечения 3-го уровня защищённости...""",
|
||||
'4': """Для обеспечения 4-го уровня защищённости персональных данных необходимо:
|
||||
— режим безопасности помещений;
|
||||
— сохранность носителей ПДн;
|
||||
— утверждение перечня лиц с доступом к ПДн;
|
||||
— использование сертифицированных СЗИ.""",
|
||||
'1': """Для значимого объекта КИИ 1-й категории значимости необходимо:
|
||||
— создание системы безопасности значимого объекта КИИ;
|
||||
— обеспечение защиты информации от компьютерных атак;
|
||||
— категорирование объектов КИИ и контроль его результатов.""",
|
||||
'2': """Для значимого объекта КИИ 2-й категории значимости необходимо:
|
||||
— создание системы безопасности значимого объекта КИИ;
|
||||
— обеспечение защиты информации от компьютерных атак.""",
|
||||
'3': """Для значимого объекта КИИ 3-й категории значимости необходимо:
|
||||
— обеспечение защиты информации от компьютерных атак.""",
|
||||
}
|
||||
return TEXTS.get(dl, TEXTS['4'])
|
||||
if category in TEXTS:
|
||||
return TEXTS[category]
|
||||
return """Для объекта КИИ, не являющегося значимым, необходимо:
|
||||
— принятие мер по защите информации, предусмотренных законодательством РФ о безопасности КИИ."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user