При работе с системой 1С:Предприятие часто возникает задача по ограничению доступа к данным для разных категорий пользователей. Механизм RLS (Record Level Security) является мощным инструментом для этих целей, позволяя управлять правами на уровне отдельных записей в базе данных. Однако, когда речь заходит о скрытии отдельных строк в табличных частях документов, сталкиваемся с определенными нюансами и ограничениями RLS, поскольку он изначально предназначен для работы с объектом целиком. В этой статье мы подробно рассмотрим, почему RLS не может напрямую скрывать строки табличной части и какие альтернативные подходы мы можем использовать для достижения желаемого результата.
Представим типовую ситуацию: у нас есть документ с табличной частью, в которой присутствует реквизит ссылочного типа, например, Реквизит1, ссылающийся на элемент справочника. Некоторые пользователи (например, финансисты) имеют полный доступ к этому документу. Другие пользователи имеют ограничения прав доступа на уровне RLS, касающиеся именно Реквизит1. При открытии такого документа в режиме "Только просмотр" ограниченный пользователь видит все строки табличной части, но для тех строк, где Реквизит1 недоступен по RLS, отображается надпись <Объект не найден>. Наша задача — не просто отобразить <Объект не найден>, а полностью скрыть эти строки.
Первое, что нам необходимо понять, это фундаментальный принцип работы механизма RLS. RLS (Record Level Security), или ограничение доступа на уровне записей, применяется к объекту базы данных в целом. То есть, когда мы настраиваем RLS, мы определяем, доступен ли конкретный документ, элемент справочника, запись регистра и т.д., для текущего пользователя. RLS не оперирует внутренними элементами объекта, такими как строки табличной части. Он не может "разрезать" документ и сделать часть его строк невидимыми, оставляя остальные видимыми.
Рассмотрим пример ограничения RLS, который часто используется для документов с табличными частями. Если мы хотим ограничить доступ к документу ЗаказИМ в зависимости от склада, указанного в табличной части Товары, мы можем написать следующее ограничение:
ЗаказИМ ГДЕ ЗаказИМ.Товары.Склад В
(ВЫБРАТЬ
ПользователиДоступныеСклады.Склад КАК Склад
ИЗ
Справочник.Пользователи.ДоступныеСклады КАК ПользователиДоступныеСклады
ГДЕ
ПользователиДоступныеСклады.Ссылка = &ТекущийПользователь)
В этом примере RLS проверяет, есть ли в табличной части ЗаказИМ.Товары хотя бы одна строка, у которой Склад соответствует разрешенным складам текущего пользователя. Если ни один склад в документе не соответствует разрешенным, то документ ЗаказИМ будет полностью недоступен. Если же хотя бы один склад соответствует разрешенным, документ будет доступен. Однако, если в этом документе будут строки с неразрешенными складами, эти склады все равно будут отображаться как <Объект не найден>, но сама строка останется видимой. Именно в этом и заключается ключевое ограничение RLS для нашей задачи.
Попытки заставить RLS скрывать отдельные строки могут привести к непредсказуемому поведению или, чаще всего, просто не дадут желаемого эффекта. Более того, скрытие отдельных строк может привести к тому, что итоговые суммы в шапке документа не будут соответствовать суммам по видимым строкам, что внесет путаницу для пользователя. Поэтому для скрытия строк нам потребуются другие механизмы, работающие на уровне пользовательского интерфейса или изменения структуры данных.
Один из наиболее прямых и часто используемых подходов для скрытия строк в табличной части документа — это применение отбора непосредственно на уровне управляемой формы. Этот метод не использует RLS для скрытия, а лишь фильтрует отображаемые строки для пользователя. Это удобно, так как мы можем динамически управлять, какие строки показывать, а какие нет.
Давайте разберем этот подход по шагам:
СтрокаДоступнаДляПросмотра.ПриСозданииНаСервере), или при изменении данных, влияющих на видимость (например, при загрузке данных документа), мы должны пройтись по всем строкам табличной части. Для каждой строки мы будем определять, доступен ли связанный Реквизит1 для текущего пользователя.Реквизит1 доступен (или если он пустой, и мы хотим отображать пустые), мы устанавливаем СтрокаДоступнаДляПросмотра = Истина. В противном случае, если Реквизит1 недоступен (и отображается <Объект не найден>), мы устанавливаем СтрокаДоступнаДляПросмотра = Ложь.Пример кода для программного отбора (концептуальный):
// В модуле формы документа
&НаСервере
Процедура ПриСозданииНаСервере(Отказ)
// ... стандартный код формы
ОпределитьВидимостьСтрокТабличнойЧасти();
// Устанавливаем отбор для табличного поля "Товары"
// Предполагается, что у табличного поля "Товары" есть элемент формы с именем "Товары"
// и в его списке вывода добавлен реквизит "СтрокаДоступнаДляПросмотра"
Элементы.Товары.ОтборСтрок.Очистить();
ОтборСтрок = Элементы.Товары.ОтборСтрок.Добавить("СтрокаДоступнаДляПросмотра");
ОтборСтрок.ВидСравнения = ВидСравнения.Равно;
ОтборСтрок.Значение = Истина;
ОтборСтрок.Использование = Истина;
// Чтобы пользователь не мог отключить отбор
Элементы.Товары.ОтборСтрок.Доступность = Ложь;
КонецПроцедуры
&НаСервере
Процедура ОпределитьВидимостьСтрокТабличнойЧасти()
Для Каждого СтрокаТЧ Из Объект.ТабличнаяЧасть1 Цикл
// Проверяем доступность реквизита.
// Здесь потребуется более сложная логика проверки прав доступа
// например, через ПолучитьПравоДоступа или аналогичные функции.
// Для простоты примера предположим, что мы можем проверить доступность напрямую
// (хотя это не всегда так, особенно с комплексным RLS).
// В реальной жизни может понадобиться обращение к серверному вызову,
// который на основе текущего пользователя и ссылки определит доступность.
ДоступенРеквизит = Истина; // По умолчанию считаем, что доступен
Если Не СтрокаТЧ.Реквизит1.Пустая() Тогда
// Пример упрощенной проверки, в реальном RLS требуется функция,
// учитывающая текущие ограничения пользователя.
// Например: ДоступенРеквизит = ОбщегоНазначения.ДоступенОбъектПользователю(СтрокаТЧ.Реквизит1);
// Или если RLS настроен на сам справочник Реквизит1, то:
Попытка
Наименование = СтрокаТЧ.Реквизит1.Наименование; // Попытка прочитать данные
Исключение
ДоступенРеквизит = Ложь; // Если чтение вызывает ошибку (например, из-за RLS)
КонецПопытки;
КонецЕсли;
СтрокаТЧ.СтрокаДоступнаДляПросмотра = ДоступенРеквизит;
КонецЦикла;
КонецПроцедуры
Преимущества:
Реквизит1 недоступен.Недостатки:
Если полное скрытие строк не является критически важным, но мы хотим сделать недоступную информацию нечитаемой, можно использовать подход маскирования. Этот метод основан на том, что RLS может ограничить доступ к полям связанного объекта, оставляя при этом ссылку на сам объект доступной (пусть и с пометкой <Объект не найден>).
Идея заключается в следующем: если у нас есть RLS на справочник Справочник1, и мы хотим, чтобы пользователи не видели наименования элементов этого справочника, но при этом могли видеть сам документ, мы можем специально исключить поле Наименование из ограничений RLS, или, наоборот, настроить RLS таким образом, чтобы оно ограничивало доступ к полям, используемым для представления объекта, кроме ссылки. При этом ссылка на объект в табличной части останется, но вместо осмысленного текста будет <Объект не найден> или просто пустая строка в колонке, если мы отключим автоматическое представление. Для быстрой генерации программного кода оформления ячеек и строк табличной части подойдёт конструктор кода условного оформления.
Пример:
Предположим, наш Реквизит1 ссылается на Справочник.Контрагенты. Если пользователь не имеет доступа к конкретному контрагенту по RLS, то в поле Реквизит1 будет <Объект не найден>. Если же мы хотим "скрыть" информацию о контрагенте, не скрывая строку, мы можем:
Справочник.Контрагенты работает таким образом, что чтение основных полей (например, Наименование) ограничено.Реквизит1 в табличном поле формы:
ПриПолученииДанных для табличного поля, чтобы программно очищать или заменять значение представления для недоступных строк.Концептуальный код для маскирования представления:
// В модуле формы документа
&НаКлиенте
Процедура ТабличнаяЧасть1ПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
Если Не ДанныеСтроки.Реквизит1.Пустая() Тогда
// Проверяем доступность связанного объекта.
// Этот вызов должен быть на сервере, чтобы избежать проблем с производительностью
// и правильно учесть RLS.
ДоступенОбъект = ПроверитьДоступностьРеквизитаНаСервере(ДанныеСтроки.Реквизит1);
Если Не ДоступенОбъект Тогда
ОформлениеСтроки.Ячейки.Реквизит1.Текст = "Доступ ограничен"; // Или пустая строка
ОформлениеСтроки.Ячейки.Реквизит1.ЦветТекста = ЦветаСтиля.ЦветНедоступногоТекста;
// Можно также сделать поле недоступным для редактирования
ОформлениеСтроки.Ячейки.Реквизит1.Доступность = Ложь;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
&НаСервере
Функция ПроверитьДоступностьРеквизитаНаСервере(СсылкаНаРеквизит)
Если СсылкаНаРеквизит.Пустая() Тогда
Возврат Истина;
КонецЕсли;
// Пытаемся получить объект. Если RLS запрещает, будет исключение или пустой объект.
// Более надежно - использовать функции проверки прав, если они есть в конфигурации (БСП).
Попытка
ОбъектРеквизита = СсылкаНаРеквизит.ПолучитьОбъект();
Возврат Истина; // Если объект получен, значит доступ есть
Исключение
Возврат Ложь; // Если возникло исключение, значит доступ ограничен
КонецПопытки;
КонецФункции;
Преимущества:
Недостатки:
Для того чтобы полностью удалить строки из табличной части формы, которые содержат ссылки на недоступные объекты, мы можем прибегнуть к программному изменению объекта документа непосредственно перед его отображением пользователю. Этот подход является более радикальным, но он позволяет достичь полного скрытия строк.
Разберем шаги:
ПриСозданииНаСервере или после загрузки данных мы проходим по всем строкам табличной части документа. Для каждой строки мы программно проверяем, имеет ли текущий пользователь право на чтение объекта, на который ссылается Реквизит1.Реквизит1 ограничен, мы удаляем соответствующую строку из табличной части документа. Важно понимать, что это изменение происходит только в объекте формы и не должно сохраняться в базе данных, если документ открыт в режиме "Только просмотр". Если же документ открыт для редактирования, и мы удаляем строки, это может привести к потере данных, если пользователь решит сохранить документ. Поэтому этот метод наиболее безопасен только для режима "Только просмотр".Пример концептуального кода для программного удаления (требует особой осторожности!):
// В модуле формы документа
&НаСервере
Процедура ПриСозданииНаСервере(Отказ)
// ... стандартный код формы
// Если документ открыт в режиме "Только просмотр"
Если Объект.ДополнительныеСвойства.Свойство("РежимПросмотра") И Объект.ДополнительныеСвойства.РежимПросмотра Тогда
ОчиститьНедоступныеСтрокиТЧ();
КонецЕсли;
// Если документ открыт в режиме редактирования, данный подход крайне рискован
// и требует подтверждения пользователя или изменения логики сохранения.
// В данном примере мы его не используем для режима редактирования.
КонецПроцедуры
&НаСервере
Процедура ОчиститьНедоступныеСтрокиТЧ()
ИндексСтроки = Объект.ТабличнаяЧасть1.Количество() - 1;
Пока ИндексСтроки >= 0 Цикл
СтрокаТЧ = Объект.ТабличнаяЧасть1[ИндексСтроки];
ДоступенРеквизит = Истина;
Если Не СтрокаТЧ.Реквизит1.Пустая() Тогда
// Здесь должна быть функция, которая проверяет права доступа к СсылкаНаРеквизит.
// Функции типа "ПолучитьПравоДоступа" или "ДоступноДляЧтения" для ссылочных объектов
// в типовых конфигурациях или БСП.
// Например:
// ДоступенРеквизит = ОбщегоНазначения.ПроверитьЧтениеОбъекта(СтрокаТЧ.Реквизит1);
// Простейшая проверка, которая может быть обманута.
// В реальных условиях необходима надежная проверка RLS.
Попытка
// Если попытка прочитать Наименование вызывает ошибку, считаем объект недоступным
Наименование = СтрокаТЧ.Реквизит1.Наименование;
Исключение
ДоступенРеквизит = Ложь;
КонецПопытки;
КонецЕсли;
Если Не ДоступенРеквизит Тогда
Объект.ТабличнаяЧасть1.Удалить(СтрокаТЧ);
КонецЕсли;
ИндексСтроки = ИндексСтроки - 1;
КонецЦикла;
КонецПроцедуры
Преимущества:
Недостатки:
Иногда наилучшим решением является не пытаться обойти ограничения RLS, а переосмыслить структуру данных и бизнес-процессы. Если проблема с видимостью строк постоянно возникает из-за различных уровней доступа, возможно, сама архитектура хранения данных не оптимальна для данных требований безопасности.
Как было предложено в одном из сообщений, "правильно делать разные документы для разных ЦФО и на них уже RLS раздавать". Если у нас есть разные центры финансовой ответственности (ЦФО), отделы, проекты или любые другие сущности, которые требуют изолированного доступа к своим данным, мы можем:
НомерНабора или ГруппаДоступа, и заполнять его. Затем RLS может быть настроен таким образом, чтобы он проверял доступность всего документа на основе этих полей (используя функции БСП типа ДляВсехСтрок() или ДляОднойИзСтрок()), но, опять же, это будет ограничивать доступ ко всему документу, а не скрывать строки.Преимущества:
Недостатки:
Библиотека стандартных подсистем (БСП) в 1С предоставляет расширенные возможности для настройки RLS, используя различные шаблоны (например, #ПоЗначениям, #ПоНаборамЗначений, #ПоЗначениямРасширенный) и функции, которые можно использовать в тексте ограничений. Такие функции, как ДляВсехСтрок(<Условие>) или ДляОднойИзСтрок(<Условие>), позволяют формулировать сложные условия для доступа к объекту на основании содержимого его табличных частей.
Однако, как мы уже неоднократно подчеркивали, даже эти продвинутые механизмы не предназначены для скрытия отдельных строк. Они влияют на доступность объекта целиком. Например:
ДляВсехСтрок(Условие): Если мы используем это в RLS, документ будет доступен только в том случае, если все строки его табличной части соответствуют условию. Если хотя бы одна строка не соответствует, весь документ станет недоступен.ДляОднойИзСтрок(Условие): Документ будет доступен, если хотя бы одна строка его табличной части соответствует условию. Если все строки не соответствуют, документ недоступен.Эти функции очень полезны для тонкой настройки доступа к документам, но они не решают проблему скрытия отдельных строк. Если документ доступен, но некоторые ссылки в его табличной части недоступны по RLS (например, ссылка на Реквизит1 в нашем случае), то в соответствующем поле все равно будет отображаться <Объект не найден>, а строка останется видимой.
Использование дополнительных регистров сведений, таких как "Наборы значений доступа" (также из БСП), может помочь в более гибком управлении сложными комбинациями прав, но опять же, это направлено на определение доступности объекта в целом, а не на его внутреннее содержимое. Для детального анализа действующих ролей, профилей и ограничений RLS можно использовать обработку для анализа и настройки прав доступа.
Выбирая подходящий подход, нам следует учитывать несколько ключевых факторов:
Механизм RLS в 1С является мощным инструментом для ограничения доступа к данным на уровне объектов. Однако, если наша задача заключается именно в скрытии отдельных строк в табличной части документа, RLS не является прямым решением. Для достижения этой цели мы должны прибегнуть к дополнительным методам:
Каждый из этих подходов имеет свои преимущества и недостатки. Мы должны тщательно проанализировать требования и возможные последствия, прежде чем выбрать наиболее подходящее решение для нашей конкретной ситуации.