Перейти к содержанию

Доступ к ДП — взаимодействие систем разграничения

Документ описывает четыре независимые системы контроля доступа к дополнительным параметрам (ДП) в 1Форме: ExtParamPermission (права группы), TaskEntityPermissionsSet (матрица доступа), SmartAccess (смарт-выражения) и колоночные права табличных ДП. Для администраторов категорий и разработчиков, настраивающих права доступа. Рассматриваются таблицы БД, задействованные в каждой системе, флаги включения и взаимодействие систем при совместной работе.

Обзор

Доступ к дополнительным параметрам контролируется тремя независимыми системами. Каждая покрывает свой уровень детализации, но документированного приоритета между ними нет.

Система 1: ExtParamPermission         — «группа видит/редактирует ДП»
Система 2: TaskEntityPermissionsSet    — «роль/группа в состоянии видит/редактирует ДП»
Система 3: SmartAccess                 — «пользователи по смарт-выражению видят/редактирует ДП»

Дополнительно для табличных ДП:

Система 4: ExtParamTableSettingsInSubcat*Permissions — «группа/состояние → доступ к колонке»

Поверх этих систем действует проверка конфиденциальности: содержимое конфиденциальной задачи отдаётся только подписчикам, независимо от прав на задачу и на сам параметр. Подробнее — в разделе «Права доступа к задачам» → «Что видно пользователю без доступа к содержимому».


Система 1: ExtParamPermission (прямые права группы)

Уровень детализации: ДП + категория + группа.

Таблица PK Колонки
ExtParamPermission (ExtParamID, SubcatID, GroupID) AllowEdit (bit)

Логика: если пользователь в группе → AllowEdit=1 → редактирование, иначе — только чтение.

Материализация: ExtParamsRights — кэш прав, пересчитывается SP ExtParamsRightsRefresh.

Где настраивается: Админка категории → ДП → вкладка «Доступ».

Особенности:

  • Нет контроля по состояниям — права статичны
  • Нет уровня Disallow — только Read/Write
  • Самая старая из трёх систем
  • При добавлении прав список выбора групп исключает группы, уже добавленные для этого ДП, — одна и та же группа не предлагается повторно, права по ней меняются в существующей строке

Система 2: TaskEntityPermissionsSet (матрица доступа)

Уровень детализации: ДП + категория + (состояние × роль/группа/действие).

Таблица Назначение
TaskEntityPermissionsSet Заголовок матрицы (EntityAccessType=1 для ДП)
TaskEntityPermissions Строки: (StateID, GroupID, ActionID, роли) → AccessType

Логика: для текущего состояния задачи определяется уровень доступа (Disallow/Read/Write) по приоритету: группы/роли/действия выше, чем «Все».

Флаг включения: ExtParamsInSubcat.TaskEntityPermissionControl (bit). Если false — система 2 не применяется.

Где настраивается: Админка категории → ДП → кнопка «Матрица доступа».

Особенности:

  • Контроль по состояниям — самая гибкая из трёх
  • Три уровня доступа: Запрет (Disallow), Чтение (Read), Запись (Write)

Список доступных ДП после перехода по маршруту. При смене статуса задачи список доступных ДП обновляется по новому статусу: ДП, ставший доступным в новом статусе, появляется сразу, без перезагрузки страницы.

Доступ к файловым ДП с историей версий. При скачивании файла из ДП типа «Файл» проверка через матрицу доступа учитывает все связи файла с задачей, включая помеченные как удалённые (IsDeleted = 1 в FileStorageFileToExtParamLinks), — это касается исторических версий файла. Доступ определяется правами пользователя на задачу, а не статусом записи связи.

Операции в гриде по защищённому полю. Ограничение доступа к ДП влияет не только на показ значения: от настройки зависит и то, какие операции доступны по его колонке — фильтр, быстрый поиск, сортировка, группировка, итоги и перечень значений фильтра. При доступе «По матрице доступа» операции доступны тому, кто читает значения параметра во всех состояниях категории, и недоступны остальным; при доступе «По SQL-функции» их закрывает только заданная пакетная функция при выключенной настройке «Просмотр не ограничивается». Полный разбор и сводная таблица «настройка доступа → доступность операций» — в логике грида. Само значение в ячейке и в карточке скрывается правами независимо от этого правила.


Система 3: SmartAccessForExtParamsInSubcat (смарт-доступ)

Уровень детализации: ДП + категория + SmartExpression → CanRead/CanWrite.

Таблица PK Назначение
SmartAccessForExtParamsInSubcat Id Правило: (SubcatId, ExtParamId?, UsersSmartExpressionId) → CanRead, CanWrite
SmartAccessForExtParamsInSubcatRules Id Список ДП, на которые действует правило
SmartAccessForExtParamsInSubcatTriggerExtParams Id При изменении каких ДП пересчитывать
SmartAccessForExtParamsInSubcatTriggerGroups Id При изменении членства в каких группах пересчитывать
SmartAccessForExtParamsInSubcatView (SmartAccessId, UserId, TaskId) Материализованный результат

Логика: SmartExpression возвращает список UserID → для них применяются CanRead/CanWrite. Результат материализуется в View-таблицу.

Флаг включения: ExtParamsInSubcat.SmartAccessControl (bit).

Где настраивается: Админка категории → ДП → «Смарт-доступ».

Окно «Смарт доступ»: наименование правила, смарт-выражение для пользователей и уровень доступа

Особенности:

  • Самая мощная: произвольная логика через SmartExpression
  • Материализованный результат — обновляется не в реальном времени
  • ExtParamId = NULL в заголовке → правило на все ДП категории
  • Триггеры пересчёта настраиваются в карточке правила смарт-доступа ДП: «Пересчёт при смене ДП» — пересчёт при изменении значений выбранных дополнительных параметров, «Пересчёт при добавлении пользователя в группу» — при изменении членства в указанных группах. Триггеры хранятся в таблицах SmartAccessForExtParamsInSubcatTriggerExtParams и SmartAccessForExtParamsInSubcatTriggerGroups. Наполнение этих таблиц из интерфейса работает начиная с версии 2.268.346 — до этого настройки триггеров в UI сохранялись только по основному правилу, а junction-таблицы триггеров оставались пустыми и автоматический пересчёт не запускался. Без настроенных триггеров пересчёт выполняется только вручную через кнопку синхронизации.
  • Материализация прав. Строки SmartAccessForExtParamsInSubcatView пересчитываются чанками по 50 задач в устойчивом порядке (сортировка по TaskId), внутри чанка строки вставляются пакетами рекомендованного размера. Удаление устаревших строк и вставка новых для одного чанка идут в одной транзакции (ReadCommitted, RequiresNew) на одном открытом соединении, поэтому сбой внутри чанка откатывает его целиком и уже записанные права не теряются. Чанк повторяется целиком до трёх попыток; если сбой повторился, обработка прерывается исключением с номером последней успешно обработанной задачи, и следующие чанки не выполняются.
  • Сериализация параллельных пересчётов. Пересчёт одного правила идёт под именованной блокировкой уровня БД: sp_getapplock в MS SQL и pg_advisory_lock в PostgreSQL. Блокировка берётся на весь проход по задачам и снимается в finally, поэтому одновременные пересчёты одного правила не пересекаются, а разные правила друг друга не ждут. Неудачное снятие блокировки пишется предупреждением в журнал и на результат записи не влияет.
  • Конфликты при записи. Повтор чанка срабатывает на любую ошибку записи. Не считается ошибкой и подавляется единственная ожидаемая гонка — нарушение внешнего ключа FK_SmartAccessForExtParamsInSubcatView_TaskId_Tasks_TaskID при выдаче прав только что созданной задаче, строка которой ещё не видна другой транзакции; остальные ошибки записи поднимаются наверх.

Схема дочерних таблиц правила:

SmartAccessForExtParamsInSubcat
  │  Id, Name, CanRead, CanWrite, UsersSmartExpressionId, ExtParamId?, SubcatId
  │
  ├── Rules (какие ДП скрывать/показывать)
  │     SmartAccessId (FK) + ExtParamId (FK → ExtParams)
  │
  ├── TriggerExtParams (при изменении каких ДП пересчитывать View)
  │     SmartAccessId (FK) + ExtParamId (FK → ExtParams)
  │
  └── TriggerGroups (при изменении членства пересчитывать View)
        SmartAccessId (FK) + GroupId (FK → Groups)

Система 4: Колоночные права табличных ДП

Уровень детализации: колонка табличного ДП + категория + (группа ИЛИ состояние).

Таблица PK Назначение
ExtParamTableSettingsInSubcat Id Привязка колонки к категории: (ExtParamInSubcatId, ColumnId) + флаги
ExtParamTableSettingsInSubcatGroupPermissions Id Доступ по группе: +GroupId → AccessType
ExtParamTableSettingsInSubcatStatePermissions Id Доступ по состоянию: +StateId → AccessType

Флаги включения (в ExtParamTableSettingsInSubcat):

  • GroupAccessControlMode (int) — режим контроля по группам
  • StateAccessControlEnabled (bit) — контроль по состояниям
  • SmartAccessControlEnabled (bit) + VisibilitySmartExpressionId (int) — контроль видимости колонки по SMART-выражению: если выражение для задачи ложно, колонка скрывается. Применяется поверх контроля по группам и состояниям (может только скрыть), при ошибке вычисления — fail-open (колонка остаётся видимой)

Ограничение: виртуальные группы не поддерживаются

При настройке доступа к колонкам таблицы по группам (через ExtParamTableSettingsInSubcatGroupPermissions) виртуальные группы не используются.

Виртуальные группы (тип «Виртуальная», например «Подписчики задач (всем)») предназначены для ограничений на уровне задач, но не для разграничения прав на уровне колонок таблиц. Для колоночных прав доступны только обычные группы (тип «Обычная» и «Связанная»).

Сочетание с доступом самого ДП

Явное право «Редактировать» на столбце (AccessType = CanEdit в ExtParamTableSettingsInSubcatGroupPermissions) снимает для этого столбца запрет «только для чтения», пришедший от групповых прав на ДП, — и только его. Остальные запреты продолжают действовать: права на задачу, закрытая задача, блокировка активной подписью, смарт-доступ без записи, «только для чтения» после заполнения, собственное ограничение столбца по статусу, а также уже рассчитанный режим столбца «Только для чтения», «Скрытая» или «Невидимая». Добавление и удаление строк остаются под доступом ДП. Признак EpTableColumnSettingsDto.EditableDespiteTableGroupReadonly один и тот же для интерфейса и для серверной проверки на сохранении, поэтому правка ячейки и импорт из Excel ведут себя одинаково. Тот же признак определяет и кнопку импорта из Excel: она показывается, когда пользователю доступен на редактирование хотя бы один столбец с включённой настройкой «Разрешить импорт». На таблице только для чтения кнопки импорта нет; экспорт при этом остаётся доступным.

Тестовое покрытие. Вызов фильтра, который на сохранении оставляет в наборе строки поднятого столбца и убирает остальные, закреплён интеграционным тестом TableEpLogic_Integration_Tests.UpdateExtParamsInTask_GroupReadonlyTable_AllowsLiftedColumnAndRejectsUnliftedColumn (Tests/TCClassLib.Test.Integration.Core/ExtParams/TableExtParam/): сохранение ячейки поднятого столбца проходит, сохранение ячейки соседнего столбца того же ДП отклоняется. Юнит-тесты покрывают только логику самого фильтра, поэтому его вызов из приватного метода сохранения проверяется на уровне интеграционного набора.


Система 5: ExtParamStateView (права по статусам)

Уровень детализации: ДП + категория + состояние (без ролей/групп — доступ общий для всех, кто видит задачу в этом статусе).

Таблица Назначение
ExtParamStateView Строка на пару (ДП × статус): CanRead, CanEdit, CanHelp

Логика: для текущего статуса задачи ищется строка ExtParamStateView по (ExtParamID, StateID). Есть строка с CanRead=1 → ДП виден; CanEdit определяет доступность редактирования.

Флаг включения: ExtParamsInSubcat.StateControl (bit). Если false — система 5 не применяется.

Где настраивается: Админка категории → ДП → кнопка «Матрица доступа» (тот же вход, что и Система 2, но отдельная сущность в БД).

Автоподдержка при изменении маршрута:

  • Удаление статуса из маршрута — orphaned-строки ExtParamStateView по исчезнувшему статусу удаляются автоматически.
  • Добавление нового статуса в маршрут — если у ДП доступ по статусам настроен на всех прежних статусах, новый статус автоматически наследует видимость: CanRead=true, а CanEdit — по консенсусу прежних статусов (если хотя бы на одном редактирование было запрещено, на новом статусе оно тоже запрещено). Если доступ был не на всех статусах — новый статус остаётся без доступа, донастройка вручную.

Особенности:

  • Не учитывает роли/группы — доступ либо есть у всех, кто видит задачу в статусе, либо нет ни у кого.
  • Права по статусам перекрывают матрицу доступа (Система 2) при конфликте.

Отображение в табличном представлении. Если в текущем статусе строки с правом на чтение нет, таблица показывает в ячейке «(нет доступа)» — так же, как для параметра с доступом «По матрице доступа». Выдача таблицы содержит запись параметра с признаком запрета чтения и без значения; значение не раскрывается ни в одном поле. То же правило действует при выгрузке таблицы в Excel и CSV: закрытая ячейка уходит пустой, содержимое файлового параметра не перечитывается. Карточка задачи и операции по колонке (фильтр, сортировка, группировка, итоги) этой правкой не менялись. То же правило действует в ленте задач и в таблице «Показ задач»: закрытая по статусу, по умному доступу или маске конфиденциальности ячейка приходит с признаком запрета чтения и без значения, список вложений в ней отсутствует. На статусы, где чтение разрешено, правило не влияет.


Взаимодействие систем

Порядок проверки и применимость систем

Какие системы действуют в разных ситуациях:

Ситуация Системы
ДП в категории, матрица выключена, SmartAccess выключен Только система 1 (ExtParamPermission)
ДП в категории, матрица включена Система 1 + Система 2
ДП в категории, SmartAccess включен Система 1 + Система 3
Колонка табличного ДП, контроль по группам или состояниям включён Система 1 + Система 4
ДП в категории, права по статусам включены (StateControl) Система 1 + Система 5
Всё включено Все пять систем

Для столбцов табличных ДП действует правило композиции: явное «Редактировать», выданное группе на столбце, снимает запрет «только для чтения» от групповых прав ДП для этого столбца — см. «Система 4», подраздел «Сочетание с доступом самого ДП». Контроль по статусам и SMART по-прежнему могут только ужесточить уже рассчитанный режим столбца.

Та же модель прав работает и при построении списка ДП, доступных для массового изменения в пакетной обработке: список читается по каждой задаче тем же способом, что и карточка (GetExtParamsByTasksUserAndSubcat), поэтому в него не попадают параметры, скрытые на статусе (ExtParamStateView.CanRead), скрытые смарт-доступом (CanReadBySmart), недоступные по группам и матрице доступа, а также параметры в режиме «только чтение» (FinalReadonly). Параметр остаётся в списке, только если доступен на всех выбранных задачах; администратор категории получает полный состав.

Проверка доступа (функция БД fn_AccessGetAccessTypeExtParam) идёт в определённом порядке, но документированного приоритета нет. Фактическое поведение:

  1. Система 3 (SmartAccess) — если включена, материализованный View определяет видимость ДП
  2. Система 2 (матрица) — если включена, определяет AccessType по состоянию/роли
  3. Система 1 (ExtParamPermission) — базовый уровень Read/Write по группе
  4. Система 4 (колоночные права) — применяется поверх для табличных ДП

Конфликт: если SmartAccess скрывает ДП (CanRead=0), но матрица разрешает Write — побеждает SmartAccess (ДП скрыт). Обратное: если матрица запрещает, а SmartAccess разрешает — ДП доступен. Фактически SmartAccess работает как маска видимости, а матрица — как уровень доступа.

Схема

Сводная схема систем доступа к ДП:

                          ┌─ ExtParamPermission (Read/Write по группе)
                          │
ExtParamsInSubcat ────────┼─ TaskEntityPermissionsSet (Disallow/Read/Write по состоянию×роль)
  │                       │     └── TaskEntityPermissions (строки матрицы)
  │                       │
  │                       └─ SmartAccessForExtParamsInSubcat (маска видимости)
  │                             ├── Rules
  │                             ├── TriggerExtParams
  │                             └── TriggerGroups
  │
  └── ExtParamTableSettingsInSubcat (для табличных ДП)
        ├── GroupPermissions (Read/Write по группе на колонку)
        └── StatePermissions (Read/Write по состоянию на колонку)