Замещение (Substitution)¶
Источники: DB_MSSQL (UserAssistant, fn_UserTaskPermissionsTemplate), core (AssistantService)
1. Что это¶
Замещение — механизм временной передачи прав от одного пользователя (принципала) другому (заместителю). Заместитель получает доступ к задачам принципала на указанный период, с ограничениями по конфиденциальности и шифрованию.
Не путать с: - Перевоплощением — администратор работает от имени пользователя (другая сессия). См. impersonation.md - Процессными помощниками — информационная запись, НЕ дающая прав (см. §8)
2. Бизнес-логика¶
Что получает заместитель¶
- Доступ к задачам принципала в разрешённых категориях (по OR-логике, как 6-й уровень доступа)
- Копии писем и комментариев принципала (кроме служебных комментариев о действиях)
- Блок «Избранное (Фамилия Имя)» в боковой панели — дерево Избранного принципала
- Тикеры принципала — счётчики задач принципала отображаются в скобках рядом с собственными
Чего НЕ получает заместитель¶
- Конфиденциальные задачи — принципала (IsConfidential=1). Полная блокировка
- Зашифрованные задачи — принципала (IsEncrypted=1). Полная блокировка
- Чаты принципала — по умолчанию исключены (CategoryInclusionMode=2, blacklist содержит ChatSubcatID)
- Скрытую оценку исполнителей — ViewSecretPerformerPoint
Матрица защиты¶
| Защитный механизм | Замещение | Перевоплощение |
|---|---|---|
| Конфиденциальность (IsConfidential) | Заблокировано | Доступ есть (by design) |
| Шифрование (IsEncrypted) | Заблокировано | Заблокировано |
| Конфиденциальность + шифрование | Заблокировано | Заблокировано |
3. Управление (пользовательский интерфейс)¶
Кто назначает¶
Пользователь назначает заместителя себе сам — раздел «Заместители» в профиле. Администратор тоже может это сделать.
Параметры назначения¶
| Параметр | Описание |
|---|---|
| Кому | Выбор заместителя |
| Период (DTFrom — DTTo) | Начало и окончание. NULL = бессрочно |
| Режим категорий | «Только категории» (whitelist) или «Кроме категорий» (blacklist) |
| Категории | Список категорий для режима |
По умолчанию — режим «Кроме категорий» (blacklist). Заместителям в этом режиме недоступны чаты и приватные задачи в категориях с шифрованием/конфиденциальностью.
Завершение замещения¶
- Принципал: кнопка «Завершить замещение» во вкладке «Заместители»
- Заместитель: кнопка «Завершить замещение» во вкладке «Заместитель у»
- Автоматически: по истечении DTTo
- Если период ещё не начался и замещение удалено вручную — запись удаляется из таблицы (с версии 265 Цефей)
Взаимное замещение¶
Допускается: можно назначить заместителем пользователя, который сам уже является замещающим.
4. Таблицы БД¶
4.1 UserAssistant (главная)¶
| Колонка | Тип | Описание |
|---|---|---|
ID |
int, PK, Identity | ID записи |
UserId |
int, FK → Users | Принципал (кого замещают) |
AssistantUserID |
int, FK → Users | Заместитель (кто замещает) |
DTFrom |
datetime, NULL | Начало периода. NULL = бессрочно |
DTTo |
datetime, NULL | Конец периода. NULL = бессрочно |
CategoryInclusionMode |
int | 1 = whitelist, 2 = blacklist |
RangeFrom |
computed | ISNULL(DTFrom, '19000101') |
RangeTo |
computed | ISNULL(DTTo, '20790606') |
IsActual |
bit | Пересчитывается по датам |
CHECK: CategoryInclusionMode IN (1, 2).
7 индексов:
- ix__...__AssistantUserID__where_IsActual — filtered WHERE IsActual=1
- ix__...__RangeTo — для пересчёта актуальности
- ix__...__UserID — filtered WHERE DTFrom IS NULL AND DTTo IS NULL (бессрочные)
- ix_fk_UserAssistant_UserId — (UserId, DTTo) INCLUDE(DTFrom, AssistantUserID, CategoryInclusionMode)
- ix_UserAssistant_AssistantUserID_RangeFrom / RangeTo — диапазонный поиск
- ux__...__ID__where_IsActual — unique filtered
4.2 UserAssistantCategories (ограничения по категориям)¶
| Колонка | Тип | Описание |
|---|---|---|
Id |
int, PK, Identity | ID записи |
AssistanceId |
int, FK → UserAssistant.ID | Ссылка на замещение |
SubcatId |
int, FK → Subcategories | Категория |
Два unique-индекса: (SubcatId, AssistanceId) и (AssistanceId, SubcatId) — оба направления поиска.
4.3 Семантика CategoryInclusionMode + UserAssistantCategories¶
| Mode | Записи в UAC | Результат |
|---|---|---|
| 1 (whitelist) | Список категорий | Заместитель видит задачи принципала только в перечисленных категориях |
| 2 (blacklist) | Список исключений | Заместитель видит задачи принципала во всех категориях кроме перечисленных |
5. SQL-функции¶
5.1 fn_GetUserAssistants¶
Возвращает всех принципалов, для которых @UserID является заместителем.
Параметры: @UserID, @include_user_itself, @convert_tz_f
Логика: SELECT FROM UserAssistant WHERE IsActual=1 AND AssistantUserID=@UserID
Возвращает: (UserId, CategoryInclusionMode, AssistanceId)
5.2 fn_GetUserAssistantsBySubcat¶
Фильтрует принципалов с учётом конкретной категории.
Параметры: @UserID, @SubcatID, @include_user_itself, @convert_tz_f
Логика:
1. Получить принципалов через fn_GetUserAssistants
2. LEFT JOIN UserAssistantCategories по AssistanceId + SubcatId
3. Фильтр:
- Mode=2 AND UAC IS NULL → категория НЕ в blacklist → доступ есть
- Mode=1 AND UAC IS NOT NULL → категория В whitelist → доступ есть
5.3 vwAllCurrentUserAssistants (view)¶
SELECT ua.UserID AS PrincipalUserID, ua.AssistantUserID,
ua.CategoryInclusionMode, ua.ID AS AssistanceId
FROM UserAssistant ua WHERE ua.IsActual = 1
Используется в DescendantSubcatAccessRefresh.
6. Интеграция с моделью прав¶
6.1 В fn_UserTaskPermissionsGeneral (через шаблон fn_UserTaskPermissionsTemplate)¶
Каждый источник доступа в шаблоне имеет два подзапроса:
1. Прямой доступ @UserID
2. Доступ через замещение (внутри маркеров --$$BEGIN ASSISTANTS$$ / --$$END ASSISTANTS$$)
Паттерн замещения в каждой ветви:
SELECT TaskID
FROM fn_GetUserAssistants(@UserID, 0, 0) uass
LEFT JOIN UserAssistantCategories uac ON uac.AssistanceId = uass.AssistanceId
AND uac.SubcatId = @SubcatID
JOIN <источник> ON <источник>.UserID = uass.UserId
LEFT JOIN vwTasksPrivate tpr ON tpr.TaskID = <источник>.TaskID
WHERE @WithoutAssistantsOnlyF = 0
AND tpr.TaskID IS NULL -- исключение конфиденциальных + зашифрованных
AND (
uass.CategoryInclusionMode = 2 AND uac.AssistanceId IS NULL -- blacklist ok
OR
uass.CategoryInclusionMode = 1 AND uac.AssistanceId IS NOT NULL -- whitelist ok
)
Ключевые элементы:
- fn_GetUserAssistants — список принципалов
- LEFT JOIN vwTasksPrivate + IS NULL — исключение конфиденциальных/зашифрованных задач
- CategoryInclusionMode whitelist/blacklist логика
- @WithoutAssistantsOnlyF=1 отключает все ветви замещения
Источники, имеющие ветвь замещения: акцептанты, смарт-доступ, подписки индивидуальные, подписки групповые, гибкие права, кастомные права (6 из 9 источников).
Не имеют ветви замещения: категорийный доступ (уже в DescendantSubcatAccess), руководители заказчиков, руководители исполнителей.
6.2 В DescendantSubcatAccessRefresh¶
SP включает замещение как отдельный источник в кэш DescendantSubcatAccess:
-- Замы, наследующие доступ к правам принципалов
SELECT uas.AssistantUserID, uascat.SubcatID,
0 AS OwnAccessSourceF, 1 AS AssistanceSourceF, 0 AS DirectorSourceF
FROM vwAllCurrentUserAssistants uas
CROSS APPLY (
-- Mode 1: whitelist
SELECT uac.SubcatId FROM UserAssistantCategories uac
JOIN @UserGroupsPermissions ugp ON ugp.SubcatID = uac.SubcatId
AND ugp.UserID = uas.PrincipalUserID
WHERE uas.CategoryInclusionMode = 1 AND uac.AssistanceId = uas.AssistanceId
UNION ALL
-- Mode 2: blacklist
SELECT ugp.SubcatId FROM @UserGroupsPermissions ugp
LEFT JOIN UserAssistantCategories uac ON uac.AssistanceId = uas.AssistanceId
AND uac.SubcatId = ugp.SubcatID
WHERE uas.CategoryInclusionMode = 2 AND ugp.UserID = uas.PrincipalUserID
AND uac.SubcatId IS NULL
) uascat
WHERE @AssistanceF = 1
Результат: если принципал имеет ActionID=8 (ViewAll) на категорию и замещение для неё разрешено — заместитель получает AssistanceSourceF=1 в кэше. Это даёт категорийный доступ без задача-по-задаче проверки.
7. ExcludeSystemSubstitutes (подписи)¶
Настройка на уровне шага маршрута подписи. Если включена (ExcludeSystemSubstitutes = true), заместитель не может подписывать вместо принципала — требуется именно тот пользователь, который указан акцептантом.
Две точки управления:
- Глобальная: SettingsCustom ключ ExcludeSystemSubstitutes
- На шаге маршрута: StatesRoutesSignatures.ExcludeSystemSubstitutes
Обе должны быть true для исключения заместителей из подписи.
Код: TaskSignaturesEntityService.cs:540, TaskSignatureActionsService.cs:1179
8. Процессные помощники¶
Не путать с замещением. Процессный помощник: - НЕ получает прав — назначение чисто информационное - Отображается в профиле пользователя - Указывает процесс, в котором помогает - Удалить может только назначивший пользователь
9. Связанная документация¶
- business.md §5 — доступ заместителя в модели 6 уровней
- database.md §11 — SQL-механика замещения в DescendantSubcatAccessRefresh
- access-check.md — fn_UserTaskPermissionsGeneral с ветвями замещения
- impersonation.md — сравнение с перевоплощением
docs/domains/signatures/— подписи и ExcludeSystemSubstitutes
10. Источники кода¶
| Объект | Файл |
|---|---|
| UserAssistant DDL | DB_MSSQL/dbo.UserAssistant.Table.sql |
| UserAssistantCategories DDL | DB_MSSQL/dbo.UserAssistantCategories.Table.sql |
| fn_GetUserAssistants | DB_MSSQL/dbo.fn_GetUserAssistants.UserDefinedFunction.sql |
| fn_GetUserAssistantsBySubcat | DB_MSSQL/dbo.fn_GetUserAssistantsBySubcat.UserDefinedFunction.sql |
| fn_UserTaskPermissionsTemplate | DB_MSSQL/dbo.fn_UserTaskPermissionsTemplate.UserDefinedFunction.sql |
| DescendantSubcatAccessRefresh | DB_MSSQL/dbo.DescendantSubcatAccessRefresh.StoredProcedure.sql |
| ExcludeSystemSubstitutes | core/TCDataAccess/Kernel/Services/Entity/Tasks/TaskSignaturesEntityService.cs:540 |