При разработке и сопровождении крупных информационных систем на платформе 1С:Предприятие 8 одним из наиболее критичных вопросов является производительность механизмов ограничения доступа на уровне записей (RLS — Record Level Security). В стандартных конфигурациях, построенных на базе 1С:Библиотеки стандартных подсистем (БСП), подсистема «Управление доступом» играет ключевую роль. Однако классическая реализация RLS часто приводит к существенному замедлению работы системы при росте объемов данных. Для комплексного анализа ролей, профилей и действующих настроек RLS подойдёт обработка для аудита прав доступа.
В этой статье мы подробно разберем, как устроена архитектура RLS в БСП, проанализируем разницу между классическим и производительным (универсальным) режимами, а также рассмотрим практические приемы оптимизации и программной подмены ограничений в формах динамических списков.
Для начала выясним причину, почему классический RLS называют неюзабельным на базах крупного размера, и как эту проблему решает универсальный производительный режим.
Классический (стандартный) режим RLS работает по принципу динамической модификации запросов СУБД непосредственно в момент обращения к базе данных. Платформа «на лету» перехватывает любой запрос пользователя к таблицам документов или справочников и добавляет к нему тяжелые соединения с регистрами сведений прав доступа (например, ТаблицыГруппДоступа, ЗначенияГруппДоступа). СУБД строит неоптимальные планы запросов, что выливается в критические зависания динамических списков, отчетов и журналов документов. В качестве альтернативного решения для ограничения видимости данных без тяжелых запросов RLS можно использовать инструмент для настройки отборов в отчетах и динамических списках.
Производительный (универсальный) режим ограничения доступа (внедренный в БСП начиная с версии 3.0) кардинально меняет логику работы системы. Рассмотрим подробнее ключевые аспекты этого механизма:
КлючиДоступаКОбъектам, КлючиДоступаКРегистрам и др.).
#Если &ОграничениеДоступаНаУровнеЗаписейУниверсально #Тогда
#ДляОбъекта("")
#Иначе
#ПоЗначениям(...)
#КонецЕсли
При включенном производительном режиме платформа подставляет простейший шаблон #ДляОбъекта(""). В результате СУБД выполняет элементарное и быстрое соединение таблицы объекта с предрассчитанной таблицей ключей доступа, что ускоряет выборку данных в десятки и сотни раз.
Компромиссы производительного режима:
ЗаполнениеДанныхДляОграниченияДоступа.Переходя на производительный режим, разработчик полностью избавляется от необходимости писать и поддерживать громоздкие шаблоны RLS в конфигураторе. Вся логика переносится непосредственно в программный код модулей менеджеров объектов.
Разберем по шагам, что требуется сделать в коде для ограничения доступа (например, для нового документа по реквизиту Организация):
Шаг 1. Описание ограничений в модуле менеджера объекта. В модуле менеджера ограничиваемого справочника или документа необходимо объявить экспортную процедуру ПриЗаполненииОграниченияДоступа:
Процедура ПриЗаполненииОграниченияДоступа(Ограничение) Экспорт
Ограничение.Текст =
"РазрешитьЧтениеИзменение ГДЕ ЗначениеРазрешено(Организация)";
КонецПроцедуры
Если логика доступа сложнее и зависит от связанных таблиц, мы можем использовать метод ПрисоединитьДополнительныеТаблицы, связывая текущую таблицу с другими объектами метаданных.
Шаг 2. Интеграция с формами. Чтобы система корректно отрабатывала проверку прав при интерактивных действиях пользователя, в модулях форм объектов (документов или справочников) необходимо прописать стандартные вызовы БСП:
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
// СтандартныеПодсистемы.УправлениеДоступом
УправлениеДоступом.ПриЧтенииНаСервере(ЭтотОбъект, ТекущийОбъект);
// Конец СтандартныеПодсистемы.УправлениеДоступом
КонецПроцедуры
&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
// СтандартныеПодсистемы.УправлениеДоступом
УправлениеДоступом.ПослеЗаписиНаСервере(ЭтотОбъект, ТекущийОбъект, ПараметрыЗаписи);
// Конец СтандартныеПодсистемы.УправлениеДоступом
КонецПроцедуры
Шаблон RLS #ПоНаборамЗначений применяется для объектов со сложной многоуровневой логикой (например, когда доступ предоставляется по условию «И» или «ИЛИ» для нескольких аналитик одновременно). Для его работы объект должен содержать табличную часть НаборыЗначенийДоступа. В формах списков этот шаблон традиционно вызывает серьезные проблемы с производительностью. Проанализируем, как можно оптимизировать или подменить этот механизм.
Это наиболее предпочтительный и штатный способ оптимизации. При активации константы ОграничиватьДоступНаУровнеЗаписейУниверсально система физически перестает связывать динамические списки с табличной частью НаборыЗначенийДоступа при чтении. Вместо этого в динамическом списке отрабатывает быстрый шаблон #ДляОбъекта(""), ссылающийся на плоскую таблицу рассчитанных ключей доступа. Сама табличная часть документа по-прежнему заполняется при записи объекта, но используется СУБД исключительно для фонового расчета ключей.
Если информационная база вынуждена работать в стандартном (не производительном) режиме, использование #ПоНаборамЗначений в динамических списках можно обойти с помощью штатного механизма БСП по замене неявных RLS-запросов на явные параметры. Выясним, как это работает:
ПриСозданииНаСервере формы списка вызывается программная настройка отборов:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ОписаниеОтборов = Новый Структура;
ОписаниеОтборов.Вставить("Организация", "Организация");
УправлениеДоступом.НастроитьОтборыДинамическогоСписка(Список, ОписаниеОтборов);
КонецПроцедуры
НастроитьОтборыДинамическогоСписка обращается к функции РазрешенныеЗначенияДляДинамическогоСписка и получает плоский массив доступных текущему пользователю элементов (например, список разрешенных организаций).ГДЕ условие вида: ОсновнаяТаблица.Организация В (&РазрешенныеОрганизации).В исключительных случаях, когда стандартные оптимизации не помогают, разработчики прибегают к полной изоляции динамического списка от механизмов RLS:
НЕ, выборки из виртуальных таблиц без параметров).