Модификация правил RLS на примере УПП 1.3

23 мая 2014

Проблема

В конфигурации "Управление производственным предприятием" редакции 1.3 настроены права доступа на уровне записей для складов. Так выглядит таблица настроек доступа на склады для двух пользователей:

Пользователь

Склад №1

Склад №2

Чтение

Запись

Чтение

Запись

Пользователь 1

V

-

V

-

Пользователь 2

V

-

V

V

То есть право на запись документов со складом "Склад №2" имеется только у пользователя "Пользователь 2". На чтение же права есть как у пользователя "Пользователь 2" так и у "Пользователь 1".

Логично предположить, что "Пользователь 2" не сможет провести документ "Перемещение товаров", где в качестве отправителя "Склад №1", а в качестве получателя "Склад №2".

Заполнение документа "Перемещение товаров"

Но типовые ограничения на уровне записей позволяют в этом случае записать/провести документ без лишних вопросов. Разберемся в причинах.

Текущее состояние

Обратимся к типовым шаблонам ограничений доступа, а именно к шаблону ограничений для операций "Изменение" и "Добавление". Права на эти операции настроены только для роли "Кладовщик".

Настройка RLS в роли "Кладовщик" для перемещения товаров

На следующем листинге приведен типовой текст шаблона ограничений на уровне складов. Обратите внимание на условие для склада отправителя и склада получателя (комментарий с восклицательными знаками). Именно из-за этого условия программа позволяет записывать/проводить документ, если у пользователя есть право на запись хотя бы на один склад, а не для обоих складов как должно быть. Листинг текста RLS правил ограничений на уровне записей для документа "Перемещение товаров":

"#Если &ИспользоватьОграничениеПоОрганизации ИЛИ &ИспользоватьОграничениеПоПодразделения ИЛИ &ИспользоватьОграничениеПоСклады #Тогда
|ТекущаяТаблица
|ИЗ
|   #ТекущаяТаблица КАК ТекущаяТаблица
|       ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ
|           СоставГруппы.Ссылка КАК ГруппаПользователей
|       ИЗ
|           Справочник.ГруппыПользователей.ПользователиГруппы КАК СоставГруппы
|       ГДЕ
|           СоставГруппы.Пользователь = &ТекущийПользователь) КАК ГруппыПользователей
|       ПО (ИСТИНА)
|ГДЕ
|НЕ ГруппыПользователей.ГруппаПользователей ЕСТЬ NULL
|И
|   (НЕ 1 В
|               (ВЫБРАТЬ ПЕРВЫЕ 1
|                   1
|               ИЗ
|                   РегистрСведений.НазначениеВидовОбъектовДоступа КАК НазначениеВидовОбъектовДоступа
|                   ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПравДоступаПользователей КАК НастройкиПравДоступаПользователей
|                       ПО
|                           ВЫБОР
|                                   #Если &ИспользоватьОграничениеПоОрганизации #Тогда
|                                   КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации)
|                                       ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.Организация
|                                   #КонецЕсли
|                                   #Если &ИспользоватьОграничениеПоПодразделения #Тогда
|                                   КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Подразделения)
|                                       ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.Подразделение
|                                   #КонецЕсли
|                                    // !!!  Условие на реквизиты документа “Перемещение товаров”.
|                                    // !!!  Согласно условию документ можно
|                                    // !!! записать, если есть право на запись хотя бы для одно
|                                    // !!! из складов. Именно из-за этого условия
|                                   #Если &ИспользоватьОграничениеПоСклады #Тогда
|                                   КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
|                                       ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа В (ТекущаяТаблица.СкладОтправитель, ТекущаяТаблица.СкладПолучатель)
|                                   #КонецЕсли
|                                КОНЕЦ
|                               И НастройкиПравДоступаПользователей.ВидОбъектаДоступа = НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
|                               И НастройкиПравДоступаПользователей.ОбластьДанных = ЗНАЧЕНИЕ(Перечисление.ОбластиДанныхОбъектовДоступа.ПустаяСсылка)
|                               И НастройкиПравДоступаПользователей.Пользователь = ГруппыПользователей.ГруппаПользователей
|               ГДЕ                 
|                    НазначениеВидовОбъектовДоступа.ГруппаПользователей = ГруппыПользователей.ГруппаПользователей
|                          И НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа В (
|                                                                                 ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.ПустаяСсылка)
|
|                                                                                 #Если &ИспользоватьОграничениеПоОрганизации #Тогда
|                                                                                 , ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации)
|                                                                                 #КонецЕсли
|                                                                                 #Если &ИспользоватьОграничениеПоПодразделения #Тогда
|                                                                                 , ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Подразделения)
|                                                                                 #КонецЕсли
|                                                                                  #Если &ИспользоватьОграничениеПоСклады #Тогда
|                                                                                 , ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
|                                                                                 #КонецЕсли
|                                                                                 )
|                   И НастройкиПравДоступаПользователей.ОбъектДоступа ЕСТЬ NULL))
|#КонецЕсли"
В шаблоне нас интересует фрагмент с условием на значения реквизитов "СкладПолучатель" и "СкладОтправитель", а именно:
...
"(ВЫБРАТЬ ПЕРВЫЕ 1
|        1
|ИЗ
| РегистрСведений.НазначениеВидовОбъектовДоступа 
|                   КАК НазначениеВидовОбъектовДоступа
|                                       
|  ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПравДоступаПользователей 
|                   КАК НастройкиПравДоступаПользователей
|     ПО
|       ВЫБОР
|                         
|  #Если &ИспользоватьОграничениеПоОрганизации #Тогда
|        КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа 
|              = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации)
|        ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа 
|              = ТекущаяТаблица.Организация
|  #КонецЕсли
|                                    
|  #Если &ИспользоватьОграничениеПоПодразделения #Тогда
|        КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа 
|              = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Подразделения)
|        ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа 
|              = ТекущаяТаблица.Подразделение
|  #КонецЕсли
|                                    
|  #Если &ИспользоватьОграничениеПоСклады #Тогда
|        КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа 
|              = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
|        ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа" 
// !!! Условие провки на право записи для складов. Если на один из складов
// !!! есть право записи, то разрешаем запись/проведение перемещения товаров
"          В (ТекущаяТаблица.СкладОтправитель, ТекущаяТаблица.СкладПолучатель)
|  #КонецЕсли
|                            
|КОНЕЦ"
...

Такая ошибка содержится не только в шаблонах ограничений для прав "Изменение" и "Добавление", но и для права "Чтение". Последнее рассматривать не будем, так как все выше и ниже сказанное справедливо и для шаблона на чтение. Для исправления ситуации можно пойти разными путями. Рассмотрим два решения.

Решение

Первое решение заключается в модификации шаблона ограничений по складам. Заменим условие:

"  #Если &ИспользоватьОграничениеПоСклады #Тогда
|        КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа 
|              = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
|        ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа 
|          В (ТекущаяТаблица.СкладОтправитель, ТекущаяТаблица.СкладПолучатель)
|  #КонецЕсли"
на:
"  #Если &ИспользоватьОграничениеПоСклады #Тогда
|        КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа 
|              = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
|        ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа 
|          = ТекущаяТаблица.СкладОтправитель
|  #КонецЕсли"
Поскольку это условие будет проверять только склад отправитель, то нужно отдельно вынести проверку условия для склада получателя. Вот листинг модифицированного шаблона проверки права записи документа "Перемещение товаров" по складам:
"#Если &ИспользоватьОграничениеПоОрганизации ИЛИ &ИспользоватьОграничениеПоПодразделения ИЛИ &ИспользоватьОграничениеПоСклады #Тогда
|ТекущаяТаблица
|ИЗ
|   #ТекущаяТаблица КАК ТекущаяТаблица
|       ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ
|           СоставГруппы.Ссылка КАК ГруппаПользователей
|       ИЗ
|           Справочник.ГруппыПользователей.ПользователиГруппы КАК СоставГруппы
|       ГДЕ
|           СоставГруппы.Пользователь = &ТекущийПользователь) КАК ГруппыПользователей
|       ПО (ИСТИНА)
|ГДЕ
|НЕ ГруппыПользователей.ГруппаПользователей ЕСТЬ NULL
|И
|   (НЕ 1 В
|               (ВЫБРАТЬ ПЕРВЫЕ 1
|                   1
|               ИЗ
|                   РегистрСведений.НазначениеВидовОбъектовДоступа КАК НазначениеВидовОбъектовДоступа
|                   ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПравДоступаПользователей КАК НастройкиПравДоступаПользователей
|                       ПО
|                           ВЫБОР
|                                   #Если &ИспользоватьОграничениеПоОрганизации #Тогда
|                                   КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации)
|                                       ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.Организация
|                                   #КонецЕсли
|                                   #Если &ИспользоватьОграничениеПоПодразделения #Тогда
|                                   КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Подразделения)
|                                       ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.Подразделение
|                                   #КонецЕсли
|                                    // !!!  Условие на реквизиты документа “Перемещение товаров”.
|                                    // !!!  Согласно условию документ можно
|                                    // !!! записать, если есть право на запись хотя бы для одно
|                                    // !!! из складов. Именно из-за этого условия
|                                   #Если &ИспользоватьОграничениеПоСклады #Тогда
|                                   КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
|                                       ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа В (ТекущаяТаблица.СкладОтправитель, ТекущаяТаблица.СкладПолучатель)
|                                   #КонецЕсли
|                                КОНЕЦ
|                               И НастройкиПравДоступаПользователей.ВидОбъектаДоступа = НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
|                               И НастройкиПравДоступаПользователей.ОбластьДанных = ЗНАЧЕНИЕ(Перечисление.ОбластиДанныхОбъектовДоступа.ПустаяСсылка)
|                               И НастройкиПравДоступаПользователей.Пользователь = ГруппыПользователей.ГруппаПользователей
|               ГДЕ                 
|                    НазначениеВидовОбъектовДоступа.ГруппаПользователей = ГруппыПользователей.ГруппаПользователей
|                          И НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа В (
|                                                                                 ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.ПустаяСсылка)

|                                                                                 #Если &ИспользоватьОграничениеПоОрганизации #Тогда
|                                                                                 , ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации)
|                                                                                 #КонецЕсли
|                                                                                 #Если &ИспользоватьОграничениеПоПодразделения #Тогда
|                                                                                 , ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Подразделения)
|                                                                                 #КонецЕсли
|                                                                                  #Если &ИспользоватьОграничениеПоСклады #Тогда
|                                                                                 , ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
|                                                                                 #КонецЕсли
|                                                                                 )
|                   И НастройкиПравДоступаПользователей.ОбъектДоступа ЕСТЬ NULL))
|#КонецЕсли"

Решение рабочее, но из-за доп. запроса для склада получателя запросы к документам "Перемещение товаров" будут менее оптимальными, чем раньше. Фактически будет выполняться два запроса к регистру "Назначение видов объектов доступа" для каждого документа "Перемещение товаров". Рассмотрим альтернативное решение с более оптимальным подходом.

Альтернативное решение

Альтернативное решение задачи заключается в создании КЭШа доступных на запись/чтение складов с помощью параметров сеанса и обращение к ним из шаблонов ограничений доступа. Тогда мы избавимся от сложных условия в шаблонах, от излишних обращений к базе данных (таблице "Назначение видов объектов доступа" и др.).

Для этого создадим в конфигурации два параметра сеанса с типом "ФиксированнымМассив":

Параметры сеанса с кэшированными  значениями доступных складовПрава доступа на параметры сеанса устанавливать не нужно, так как использоваться они будут только в шаблонах RLS, а в них доступ к параметрам осуществляется в привилегированном режиме.

При запуске программы нам лишь нужно инициировать параметры сеанса для получения доступных складов. Создадим общий серверный модуль "YY_Дополнительно" с возможностью вызова сервера.  В нем создадим экспортную процедуру "ИнициироватьКэшДоступныхЗначенияОграниченийНаУровнеЗаписей()", которую будем вызывать в модуле сеанса. А именно добавить в процедуру "УстановитьПараметрыМеханизмаОграниченияПравДоступа()" общего модуля "ПолныеПрава" вызов нашей процедуры:

Процедура УстановитьПараметрыМеханизмаОграниченияПравДоступа() Экспорт
 
 // ...   ...
    
 УстановитьПараметрСеансаТекущиеУчетныеЗаписиНалогоплательщика();
 
 // ++ YPermitin - Инициируем кэш доступных значений 
 // для ограничений на уровне записей
 YY_ДополнительноСервер.
  ИнициироватьКэшДоступныхЗначенияОграниченийНаУровнеЗаписей();
 
КонецПроцедуры 

Процедура "УстановитьПараметрыМеханизмаОграниченияПравДоступа()" вызывается при установке параметров сеанса. 

На следующем листинге представлен полный код процедуры "ИнициироватьКэшДоступныхЗначенияОграниченийНаУровнеЗаписей()" для получения кэшированных значений и инициализации параметров сеанса:

Процедура ИнициироватьКэшДоступныхЗначенияОграниченийНаУровнеЗаписей() Экспорт
        
        // Устанавливаем привилигированный режим, т.к. права доступа на 
        // параметры сеанса не установлены
        УстановитьПривилегированныйРежим(Истина);
        
        // Начальная инициализация параметров сеанса
        ПустойФиксМассив = Новый ФиксированныйМассив(Новый Массив);
        ПараметрыСеанса.YY_ДоступныеСкладыЧтение = ПустойФиксМассив;
        ПараметрыСеанса.YY_ДоступныеСкладыЗапись = ПустойФиксМассив;
        
        // Если ограничения по складам включены 
        Если ПараметрыСеанса.ИспользоватьОграничениеПоСклады = Истина Тогда
                
                // Получаем склады, на которые есть доступ на запись
                ЗапросЗаписьСклады = Новый Запрос;
                ЗапросЗаписьСклады.Текст = 
                "ВЫБРАТЬ
                |        ТекущаяТаблица.Ссылка
                |ИЗ
                // Условия на отбираемые склады взято из текста ограничений на уровне записей для складов
                // из роли "Пользователь"
                |        Справочник.Склады КАК ТекущаяТаблица
                |                ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ
                |                        СоставГруппы.Ссылка КАК ГруппаПользователей
                |                ИЗ
                |                        Справочник.ГруппыПользователей.ПользователиГруппы КАК СоставГруппы
                |                ГДЕ
                |                        СоставГруппы.Пользователь = &ТекущийПользователь) КАК ГруппыПользователей
                |                ПО (ИСТИНА)
                |ГДЕ
                |        (ТекущаяТаблица.ЭтоГруппа
                |                        ИЛИ НЕ ГруппыПользователей.ГруппаПользователей ЕСТЬ NULL 
                |                                И НЕ 1 В
                |                                                (ВЫБРАТЬ ПЕРВЫЕ 1
                |                                                        1
                |                                                ИЗ
                |                                                        РегистрСведений.НазначениеВидовОбъектовДоступа КАК НазначениеВидовОбъектовДоступа
                |                                                                ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПравДоступаПользователей КАК НастройкиПравДоступаПользователей
                |                                                                ПО
                |                                                                        НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.Ссылка
                |                                                                                И НастройкиПравДоступаПользователей.ВидОбъектаДоступа = НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
                |                                                                                И НастройкиПравДоступаПользователей.ОбластьДанных = ЗНАЧЕНИЕ(Перечисление.ОбластиДанныхОбъектовДоступа.ПустаяСсылка)
                |                                                                                И НастройкиПравДоступаПользователей.Пользователь = ГруппыПользователей.ГруппаПользователей
                // Для проверки на возможность записи нужно добавить следующее условие:                                   
                |                                                                                И НастройкиПравДоступаПользователей.Запись = ИСТИНА
                // Если нужно проверить только на чтение, то достаточно убрать добавленное условие.
                |                                                ГДЕ
                |                                                        НазначениеВидовОбъектовДоступа.ГруппаПользователей = ГруппыПользователей.ГруппаПользователей
                |                                                        И НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа В (ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады))
                |                                                        И НастройкиПравДоступаПользователей.ОбъектДоступа ЕСТЬ NULL ))";
                ЗапросЗаписьСклады.УстановитьПараметр("ТекущийПользователь", глЗначениеПеременной("глТекущийПользователь")); 
                РезультатЗапросаЗаписьСклады = ЗапросЗаписьСклады.Выполнить();   
                ВыборкаЗаписьСклады = РезультатЗапросаЗаписьСклады.Выбрать();
                МассивДоступныхСкладовЗапись = Новый Массив;
                Пока ВыборкаЗаписьСклады.Следующий() Цикл
                        МассивДоступныхСкладовЗапись.Добавить(ВыборкаЗаписьСклады.Ссылка);
                КонецЦикла;
                ПараметрыСеанса.YY_ДоступныеСкладыЗапись = Новый ФиксированныйМассив(МассивДоступныхСкладовЗапись);
                
                // Получаем склады с правами на чтение. Запрос аналогичный за исключением условия:
                //          "И НастройкиПравДоступаПользователей.Запись = ИСТИНА"                
                ЗапросЧтениеСклады = Новый Запрос;
                ЗапросЧтениеСклады.Текст = 
                "ВЫБРАТЬ
                |        ТекущаяТаблица.Ссылка
                |ИЗ
                |        Справочник.Склады КАК ТекущаяТаблица
                |                ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ
                |                        СоставГруппы.Ссылка КАК ГруппаПользователей
                |                ИЗ
                |                        Справочник.ГруппыПользователей.ПользователиГруппы КАК СоставГруппы
                |                ГДЕ
                |                        СоставГруппы.Пользователь = &ТекущийПользователь) КАК ГруппыПользователей
                |                ПО (ИСТИНА)
                |ГДЕ
                |        (ТекущаяТаблица.ЭтоГруппа
                |                        ИЛИ НЕ ГруппыПользователей.ГруппаПользователей ЕСТЬ NULL 
                |                                И НЕ 1 В
                |                                                (ВЫБРАТЬ ПЕРВЫЕ 1
                |                                                        1
                |                                                ИЗ
                |                                                        РегистрСведений.НазначениеВидовОбъектовДоступа КАК НазначениеВидовОбъектовДоступа
                |                                                                ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПравДоступаПользователей КАК НастройкиПравДоступаПользователей
                |                                                                ПО
                |                                                                        НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.Ссылка
                |                                                                                И НастройкиПравДоступаПользователей.ВидОбъектаДоступа = НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
                |                                                                                И НастройкиПравДоступаПользователей.ОбластьДанных = ЗНАЧЕНИЕ(Перечисление.ОбластиДанныхОбъектовДоступа.ПустаяСсылка)
                |                                                                                И НастройкиПравДоступаПользователей.Пользователь = ГруппыПользователей.ГруппаПользователей
                |                                                ГДЕ
                |                                                        НазначениеВидовОбъектовДоступа.ГруппаПользователей = ГруппыПользователей.ГруппаПользователей
                |                                                        И НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа В (ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады))
                |                                                        И НастройкиПравДоступаПользователей.ОбъектДоступа ЕСТЬ NULL ))";
                ЗапросЧтениеСклады.УстановитьПараметр("ТекущийПользователь", глЗначениеПеременной("глТекущийПользователь")); 
                РезультатЗапросаЧтениеСклады = ЗапросЧтениеСклады.Выполнить();   
                ВыборкаЧтениеСклады = РезультатЗапросаЧтениеСклады.Выбрать();
                МассивДоступныхСкладовЧтение = Новый Массив;
                Пока ВыборкаЧтениеСклады.Следующий() Цикл
                        МассивДоступныхСкладовЧтение.Добавить(ВыборкаЧтениеСклады.Ссылка);
                КонецЦикла;
                ПараметрыСеанса.YY_ДоступныеСкладыЧтение = Новый ФиксированныйМассив(МассивДоступныхСкладовЧтение);
                
        КонецЕсли;

КонецПроцедуры

В результате текст условий ограничений для складов значительно упроститься по сравнению с предыдущей версией. Так он будет выглядеть для складов:

Условия на запись/добавление:

"ПеремещениеТоваров ГДЕ 
| ПеремещениеТоваров.СкладОтправитель В (&YY_ДоступныеСкладыЗапись)
| И ПеремещениеТоваров.СкладПолучатель В (&YY_ДоступныеСкладыЗапись)"

 

Условия на чтение:

"ПеремещениеТоваров ГДЕ 
| ПеремещениеТоваров.СкладОтправитель В (&YY_ДоступныеСкладыЧтение)
| И ПеремещениеТоваров.СкладПолучатель В (&YY_ДоступныеСкладыЧтение)"

За счет упрощения запроса нам больше не нужно постоянно проверять права на чтение/запись по таблице "Назначения видов объектов доступа". Вместо этого мы будем использовать КЭШ доступных значений.

Разумеется, чтобы сохранить всю функциональность (ограничения по организациями, подразделениям и т.д.) нужно либо эти значения переводить в КЭШ, либо исправить текст условий, чтобы для этих видов ограничений оставался старый текст условий для проверки ограничений.

Заключение

Мы рассмотрели два варианта решения проблемы с типовыми правами доступа на уровне складов для документа "Перемещение товаров". Первое решение было основано на изменении текста условий ограничений. Условия были разделены на два отдельных запроса. Вариант решения рабочий, но может создать доп. нагрузку на сервер СУБД и замедлить работу.

Альтернативный вариант - это кэширование доступных значений и обращение к ним уже непосредственно в тексте условий RLS. Запросы к базе данных значительно упрощаются за счет кэширования, однако есть и минус в данном подходе - это потеря гибкости. Если ранее при изменении настроек прав доступа на уровне записей изменения вступали для всех пользователей сразу, то теперь новые правила вступят в силу только после перезапуска сеанса, т.к. старые значения в параметрах сеанса останутся и нужно будет их инициализировать заново. Эта проблема решаемая, но в рамках этой статьи ее рассматривать не будем.

Ограничения на уровне записей в действии

В УПП 1.3 это не единственная ошибка с ограничениями на уровне складов. Интересное поведение можно заметить для регистра "УчетЗатратРегл" и еще множество других моментов. Работать можно, но иногда очень сильно мешают недоработка этих правил.

И напоследок модифицированные тексты условий на чтение и запись документа "Перемещение товаров" сделанные в первом решении:

  1. Правила на чтение.
  2. Правила на запись/добавление.

В последующих статьях рассмотрим влияние RLS на взаимодействие платформы с СУБД.


comments powered by Disqus