Оглавление
Отличительная особенность производительного режима RLS
Несколько конкретных примеров внедрения
Для гибкой настройки прав пользователей в конфигурациях на базе БСП (подсистема УправлениеДоступом) реализован механизм настройки доступа на уровне записей (RLS). Этот механизм позволяет ограничивать доступ не только по видам объектов (для конкретных справочников, документов…), но и по данным в этих объектах (т.е., например, ограничить список документов только по одной организации или по конкретному менеджеру).
Функционал подсистемы УправлениеДоступом позволяет работать с RLS в двух режимах: стандартном и производительном. Каждый из режимов имеет свои преимущества и недостатки относительно другого. Основные из них будут рассмотрены в тексте далее.
Для дальнейших скриншотов, примеров кода, а также для решения задачи будем использовать демонстрационную конфигурацию "Библиотека стандартных подсистем", редакция 3.1 (3.1.5.132).
Ограничения RLS описываются в роли для конкретного объекта. Например, ограничение на чтение документа _ДемоСчетНаОплатуПокупателю в роли
_ДемоЧтениеДокументовПокупателей описывается следующим образом:
Как мы видим, для описания ограничений используются специальные шаблоны. В данном случае #ДляОбъекта и #ПоЗначениям. Шаблоны, которые могут быть использованы в роли необходимо описать на вкладке “Шаблоны ограничений”:
В свою очередь, эти шаблоны можно скопировать из поставляемой роли ИзменениеУчастниковГруппДоступа.
Рассмотрим текст ограничения доступа подробнее.
#Если &ОграничениеДоступаНаУровнеЗаписейУниверсально #Тогда
#ДляОбъекта("")
#Иначе
#ПоЗначениям( "Документ._ДемоСчетНаОплатуПокупателю", "","",
"_ДемоОрганизации", "Организация",
"_ДемоГруппыПартнеров","Партнер", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","")
#КонецЕсли
Как мы видим, выбор шаблона зависит от параметра &ОграничениеДоступаНаУровнеЗаписейУниверсально, который как раз и определяет режим, в котором должно использоваться RLS — стандартный или производительный. Значение этого параметра хранится в параметрах сеанса, а в самой базе выбрать режим можно двумя способами:
- Зайти в раздел Администрирование - Настройки пользователей и прав - Группы доступа - Вариант работы - Производительный
- В окне “Все функции” изменить значение константы “Ограничивать доступ на уровне записей универсально” на ИСТИНА, если нужен производительный режим и, соответственно на ЛОЖЬ, если стандартный.
Соответственно, если &ОграничениеДоступаНаУровнеЗаписейУниверсально = ЛОЖЬ, то будет использоваться стандартный метод и выполняться код
#ПоЗначениям( "Документ._ДемоСчетНаОплатуПокупателю", "","",
"_ДемоОрганизации", "Организация",
"_ДемоГруппыПартнеров","Партнер", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","", "","", "",""),
а если &ОграничениеДоступаНаУровнеЗаписейУниверсально = ИСТИНА, то будет использоваться производительный метод и выполняться код
#ДляОбъекта("").
Примечание. Подробнее о синтаксисе и использовании шаблонов можно почитать в их описании.
При стандартном режиме ограничение прописывается прямо в роли (установлен отбор по разрешенным организации и партнеру). Эти отборы добавляются к итоговому запросу при обращении к СУБД. При наличии нескольких ролей с описанными ограничениями все условия собираются в один запрос, растет количество левых соединений, повышается риск неправильного использования индексов, выбора неоптимального плана выполнения запроса, что значительно его усложняет и замедляет выполнение. А так как при стандартном режиме работы ограничения рассчитываются “на лету” при каждом обращении к данным (формирование списков справочников и документов, их открытие и записи), то работа пользователя может значительно замедлиться из-за сложности сформированного запроса.
Эту проблему решает включение производительного режима. При нём все отборы и ограничения не добавляются в запрос, раздувая его, а предварительно рассчитываются в некоторые ключи доступа и в итоговый запрос попадают только они, что увеличивает производительность. О ключах и их расчете подробнее далее.
Как мы видим, в ограничении роли для производительного режима не описаны никакие условия, нет никакой информации о видах доступа, по которым необходимо ограничивать данные. Где же они указаны? Для этого в подсистеме УправлениеДоступом предусмотрена специальная экспортная процедура ПриЗаполненииОграниченияДоступа(), который описывается в модуле менеджера объекта, указанного в роли.
Так, для документа _ДемоСчетНаОплатуПокупателю эта процедура описана следующим образом:
Процедура ПриЗаполненииОграниченияДоступа(Ограничение) Экспорт
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ЗначениеРазрешено(Организация)
| И ЗначениеРазрешено(Партнер)";
…
КонецПроцедуры
Здесь мы видим, что ограничение описывается с помощью некоторых типов ограничения (РазрешитьЧтениеИзменение) и функций ограничений (ЗначениеРазрешено()). Это нужно понимать так: пользователю будет разрешено читать и изменять документ _ДемоСчетНаОплатуПокупателю, когда ему будут разрешены значения организации и партнера, указанные в этом документе.
Примечание. Подробно о типах, функциях, синтаксисе ограничений описано на сайте ИТС в разделе - Инструкции по разработке на 1С
— Библиотека стандартных подсистем 3.1.6. Документация
— Глава 3. Настройка и использование подсистем при разработке конфигурации
— Управление доступом
— Разработка ограничений прав доступа
1С:ИТС Разработка ограничений прав доступа
Далее в статье рассмотрим кратко лишь некоторые.
Некоторые типы ограничений
Типы ограничения могут быть:
- РазрешитьЧтениеИзменение
- РазрешитьИзменениеЕслиРазрешеноЧтение
- РазрешитьИзменение
Основным способом описания ограничений необходимо считать функцию ЗначениеРазрешено(). Функция выполняет поиск проверяемого значения среди разрешенных значений в группах доступа, профили которых предоставляют соответствующие права на список.
- ЗначениеРазрешено(<Реквизит> [<проверяемые типы>] [, <уточнение сравнения 1> [, <уточнение сравнения 2>] …]), где проверяемые типы могут быть (значения непроверяемых типов запрещены, если не уточнены отдельно):
- ТОЛЬКО <Имя таблицы>;
- ТОЛЬКО (<Имя таблицы 1>, <Имя таблицы 2>, … );
- КРОМЕ <Имя таблицы>;
- КРОМЕ (<Имя таблицы 1>, <Имя таблицы 2>, … );
- Уточнение сравнения может быть:
- ПустаяСсылка КАК Ложь/Истина;
- Неопределено КАК Ложь/Истина;
- Null КАК Ложь/Истина;
- Отключено КАК Ложь/Истина (только для функции ЗначениеРазрешено);
- <Таблица> КАК Ложь/Истина (например, Справочник.Проекты КАК Истина).
Также существуют следующие функции:
- ЧтениеОбъектаРазрешено
- ЧтениеСпискаРазрешено
- ИзменениеОбъектаРазрешено
- ИзменениеСпискаРазрешено
Синтаксис этих функций аналогичен функции ЗначениеРазрешено().
Эти функции необходимо использовать, когда необходимо установить доступность (чтение/изменение) объекта в зависимости от доступности его реквизита. Например, документ _ДемоСчетНаОплатуПокупателю должен быть доступен, когда доступен его реквизит Организация. В таком случае ограничение будет описано как:
Процедура ПриЗаполненииОграниченияДоступа(Ограничение) Экспорт
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ЧтениеОбъектаРазрешено(Организация)";
КонецПроцедуры
- ДляВсехСтрок(<Условие>)
Функция выполняет проверку в строках с помощью логического «И». Используется для одновременного выполнения условия во всех строках табличной части объекта.
- ДляОднойИзСтрок(<Условие>)
Функция выполняет проверку в строках с помощью логического «ИЛИ». Используется для выполнения условия хотя бы в одной строке табличной части объекта.
Примеры возможных ограничений
- Организация и контрагент в шапке документа:
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ЗначениеРазрешено(Организация)
|И ЗначениеРазрешено(Контрагент)";
- Организация в шапке документа, контрагент в табличной части, достаточно одного разрешенного контрагента:
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ЗначениеРазрешено(Организация)
|И ЗначениеРазрешено(Поставщики.Контрагент)";
- Организация в шапке документа, контрагент в табличной части, достаточно одного разрешенного контрагента (если табличная часть пустая, тогда доступ по контрагенту разрешен):
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ЗначениеРазрешено(Организация)
|И ЗначениеРазрешено(Поставщики.Контрагент, Null КАК Истина)";
- Организация в шапке документа, контрагент в табличной части, и требуется, чтобы все контрагенты были разрешены (если табличная часть пустая, тогда доступ по контрагенту запрещен):
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ЗначениеРазрешено(Организация)
|И ДляВсехСтрок(ЗначениеРазрешено(Поставщики.Контрагент))";
- Организация и контрагент в табличной части, при этом достаточно, чтобы любая из пар организации с контрагентом была разрешена:
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ЗначениеРазрешено(Поставщики.Организация)
|И ЗначениеРазрешено(Поставщики.Контрагент)";
- Организация и контрагент в табличной части, при этом требуется, чтобы все пары организации с контрагентом были разрешены:
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ДляВсехСтрок(
| ЗначениеРазрешено(Поставщики.Организация)
| И ЗначениеРазрешено(Поставщики.Контрагент))";
- Организация и контрагент в табличной части, при этом требуется, чтобы одна из организаций и один из контрагентов были разрешены:
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ДляОднойИзСтрок(ЗначениеРазрешено(Поставщики.Организация))
|И ДляОднойИзСтрок(ЗначениеРазрешено(Поставщики.Контрагент))";
- Отправитель – измерение составного типа, при этом требуется проверять только ссылки Справочник.Склады:
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ЗначениеРазрешено(Отправитель ТОЛЬКО Справочник.Склады)";
Отличительная особенность производительного режима RLS
Вернёмся к основной отличительной особенности производительного режима RLS. Она заключается в том, что в этом режиме расчет прав происходит предварительно и записывается в специальные таблицы (справочник и регистры сведений). Это позволяет достичь высокой производительности запросов с RLS, так как добавляет простой и статический фрагмент к текстам запросов в ролях. За счет этого обеспечивается одинаково хорошая скорость работы при различной прикладной логике ограничений доступа, при различных условиях и их комбинациях. Но, так как предварительный расчет прав доступа занимает некоторое время, поэтому изменения в правах вступают в силу с некоторой задержкой.
Для хранения этих предварительно рассчитанных данных (ключей) используются следующие таблицы:
- справочник КлючиДоступа
- регистр сведений КлючиДоступаКОбъектам
- регистр сведений КлючиДоступаПользователей
Справочник КлючиДоступа имеет структуру:
В реквизитах Значение1 - Значение5 хранятся комбинации конкретных значений доступа. В зависимости от ограничений каждого конкретного типа объектов конфигурации состав значений разный. Например, как упоминалось выше, для документа _ДемоСчетНаОплатуПокупателю определено ограничение по Организации и Партнеру. Соответственно, будут сформированы ключи с возможными комбинациями организации и партнера.
В реквизит Хэш реквизит рассчитывается специальный хеш функции. Он позволяет быстро понять, существует ли уже в системе ключ с такой комбинацией значений или нет.
Структура регистров следующая:
Причем Объект РС КлючиДоступаКОбъектам имеет тип ОпределяемыйТип.ВладелецЗначенийКлючейДоступа, которые содержит в себе ссылки на все объекты, которые могут быть ограничены с помощью RLS.
Регистр ключей доступа к объектам, а также сами ключи доступа обновляются регламентно. Регистр ключей доступа пользователей в измерении Пользователь содержит справочник ключей пользователей, к которым применяется ограничение, а также справочник ключей доступа к объектам.
Таким образом при проверке ограничений доступа вне зависимости от количества настроенных для конкретного проверяемого объекта видов доступа запрос всегда будет дополняться одним соединением с регистром «Ключи доступа к объектам» по проверяемому объекту и с регистром «Ключи доступа пользователей» по текущему пользователю:
Обновление ключей объектов и ключей доступа к ним выполняются регламентным заданием. А также ого может быть запущено из обработки “Обновление доступа на уровне записей”:
Эта обработка позволяет запускать процесс обновления ключей вручную, а также настраивать и контролировать процесс выполнения.
- Запуск обработки выполняется по кнопке
- Можно настроить обновление ключей по определенным объектам. Эта настройка выполняется по кнопке Ещё - Ручное управление… На этой форме можно выбрать конкретные справочники, документы или регистры, по которым необходимо обновить ключи, отметить их флажками, после этого запланировать обновление и запустить обработку:
- Также есть возможность выводить более подробную информацию о ходе выполнения обработки
Для удобного написания и контроля текстов ограничений в комплекте поставки есть специальная обработка УправлениеДоступом:
Она позволяется в пользовательском режиме, без редактирования конфигурации формировать и проверять тексты ограничения доступа для объектов. Для этого необходимо во вкладке “Разработка ограничений доступа” необходимо выбрать список, ограничение на который нужно отредактировать:
После этого на форму загрузится текст ограничений из процедуры ПриЗаполненииОграниченияДоступа, а также шаблон ограничения в роли, а также кнопки для проверки текста вставки его в код:
При наличии ошибки в тексте ограничения и нажатии на кнопку “Проверить” появляется вкладка “(ошибки)”, в которой описаны ошибка как самого текста, так и всего внедрения механизма ограничений по записям для объекта:
Несколько конкретных примеров внедрения
Допустим, нам необходимо создать новый документ и подключить его к механизму ограничений RLS. Для этого необходимо выполнить следующие операции:
— создать новый документ ТестовыйДокументСОграничениями
— добавить объект в определяемые типы ВладелецЗначенийКлючейДоступа и ВладелецЗначенийКлючейДоступаДокумент.
Примечание. В определяемый тип ВладелецЗначенийКлючейДоступаДокумент добавляются документы. Справочники нужно добавить в ВладелецЗначенийКлючейДоступаОбъект, регистры сведений - в ВладелецЗначенийКлючейДоступаНаборЗаписей, регистры расчета - в ВладелецЗначенийКлючейДоступаНаборЗаписейРегистраРасчета.
— в процедуру ПриЗаполненииСписковСОграничениемДоступа общего модуля УправлениеДоступомПереопределяемый вставить текст
Списки.Вставить(Метаданные.Документы.ТестовыйДокументСОграничениями, Истина);
Процедура ПриЗаполненииСписковСОграничениемДоступа(Списки) Экспорт
…
Списки.Вставить(Метаданные.Документы.ТестовыйДокументСОграничениями, Истина);
КонецПроцедуры
— на форме документа в обработчики ПриЧтенииНаСервере и ПослеЗаписиНаСервере добавить следующий код:
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
// СтандартныеПодсистемы.УправлениеДоступом
Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
МодульУправлениеДоступом = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступом");
МодульУправлениеДоступом.ПриЧтенииНаСервере(ЭтотОбъект, ТекущийОбъект);
КонецЕсли;
// Конец СтандартныеПодсистемы.УправлениеДоступом
КонецПроцедуры
&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
// СтандартныеПодсистемы.УправлениеДоступом
УправлениеДоступом.ПослеЗаписиНаСервере(ЭтотОбъект, ТекущийОбъект, ПараметрыЗаписи);
// Конец СтандартныеПодсистемы.УправлениеДоступом
КонецПроцедуры
— создать новые роли для чтения и добавления/изменения нового документа: ЧтениеТестовыхДокументовСОграничениями и ДобавлениеИзменениеТестовыхДокументовСОграничениями. Для них определить соответствующие права (чтение, добавление, изменение), скопировать шаблон ограничений ДляОбъекта() из роли ИзменениеУчастниковГруппДоступа и прописать код ограничения доступа для необходимых прав:
#Если &ОграничениеДоступаНаУровнеЗаписейУниверсально #Тогда
#ДляОбъекта("")
#Иначе
ИСТИНА
#КонецЕсли
Примечание. В данном конкретном случае принимаем, что производительный режим RLS используется, поэтому ограничение для обычного режима не прописываем и в соответствующей ветке прописываем просто ИСТИНА.
- Нужно для этого документа реализовать ограничение, чтобы были доступны только те документы, в которых доступны организация и подразделение. Необходимо выполнить следующие действия:
— в модуле менеджера документа в процедуре ПриЗаполненииОграниченияДоступа прописать текст ограничения:
Процедура ПриЗаполненииОграниченияДоступа(Ограничение) Экспорт
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ЗначениеРазрешено(Организация)
| И ЗначениеРазрешено(Подразделение)";
КонецПроцедуры
— в пользовательском режиме создать профили доступа с созданными ранее ролями, группы доступа и прописать в них ограничения по организациям и подразделениям и назначить их пользователям:
— создать документы и запустить задание обновления ключей доступа.
В результате получим ограниченный список документов для тестового менеджера:
под полными правами
под ограниченными правами
- Нужно реализовать ограничение, чтобы были доступны только те документы, в которых доступны организация и было доступно хотя бы одно место хранения из ТЧ Запасы.
— в модуле менеджера документа в процедуре ПриЗаполненииОграниченияДоступа прописать текст ограничения:
Процедура ПриЗаполненииОграниченияДоступа(Ограничение) Экспорт
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ЗначениеРазрешено(Организация)
| И ДляОднойИзСтрок(ЗначениеРазрешено(Запасы.МестоХранения))";
КонецПроцедуры
— добавить вид ограничения в профиль доступа, прописать в группе доступа ограничения по местам хранения, обновить ключи:
В результате под пользователем с ограниченными правами увидим только те документы, у которых доступна организация и в ТЧ Запасы хотя бы в одной строке указан Розничный склад:
- Рассмотрим пример, когда необходимо реализовать ограничение по какому-то новому реквизиту, тип которого не описан в доступных видах доступа. Реализуем такую задачу. Для этого нужно выполнить следующее:
— создать новый справочник ТестовыйРегион. Выполнить для него все те же операции, что и для самого документа ТестовыйДокументСОграничениями (роли можно создать новые или прописать в какие-то существующие, например для чтения/изменения документа. В данном примере это не принципиально. В ПриЗаполненииОграниченияДоступа прописать ограничение на само себя: ЗначениеРазрешено(Ссылка)).
— добавить ссылку и объект ТестовыйРегион в определяемые типы соотстветственно ЗначениеДоступа и ЗначениеДоступаОбъект
— в процедуру ПриЗаполненииВидовДоступа общего модуля УправлениеДоступомПереопределяемый вставить текст:
Процедура ПриЗаполненииСписковСОграничениемДоступа(Списки) Экспорт
…
ВидДоступа = ВидыДоступа.Добавить();
ВидДоступа.Имя = "ТестовыйРегион";
ВидДоступа.Представление = НСтр("ru = 'Тестовый регион'");
ВидДоступа.ТипЗначений = Тип("СправочникСсылка.ТестовыйРегион");
КонецПроцедуры
— запустить обработку ОбновлениеВспомогательныхДанных (из Инструментов разработчика) и обновить всё.
— добавить новый вид доступа в профиль доступа и определить конкретные ограничения в группе доступа:
— добавить реквизит в документ, вывести на форму, заполнить (скриншот от пользователя с полными правами):
— изменить текст ограничения в процедуре ПриЗаполненииОграниченияДоступа документа ТестовыйДокументСОграничениями, чтобы он учитывал новый реквизит:
Процедура ПриЗаполненииОграниченияДоступа(Ограничение) Экспорт
Ограничение.Текст =
"РазрешитьЧтениеИзменение
|ГДЕ
| ЗначениеРазрешено(Организация)
| И ЗначениеРазрешено(ТестовыйРегион)";
КонецПроцедуры
— обновить ключи доступа.
В результате на пользователя с ограниченными правами распространяются ограничения по организациям (ООО Тестовая организация и Новые технологии ООО) и регионам (Центральный и Южный) и он получит такой список документов: