В самом начале
Несмотря на то, что конфигурация "Управление производственным предприятием", или просто УПП, считается устаревшей, а фирма "1С" исключила ее из продажи, фактически она будет еще долго использоваться бизнесом. За все время существования, продукт обрел своих клиентов, сделана масса внедрений, потрачены тысячи и более часов на доработки. Можно быть уверенным, что конфигурация проживет еще ни одно десятилетие, ведь со многими задачами она до сих пор успешно справляется.
Конечно, сейчас уже вовсю идут внедрения ERP и знания конфигурации УПП становятся все менее актуальными. Но, думаю, все же у многих будет необходимость еще поработать с ней, а значит и материалы этой статьи пригодятся.
Давным давно мной был создан блог DevelPlatform, в котором были статьи по разработке на платформе 1С, администрированию, онлайн-инструменты, а также немного о платформе .NET.
Больше года назад сайт был закрыт. Некоторые из его материалов будут реанимированы на Инфостарт.
В подборке материалов мы коснемся как внутреннего устройства функционала администрирования пользователей в конфигурации, так и его настройки / использования в режиме 1С:Предприятие.
В память об УПП! Ура, товарищи!
ВНИМАНИЕ!!! Лонгрид! Очень много букв!
Роли
Конфигурация "Управление производственным предприятием" имеет широкие возможности для настройки прав доступа пользователей в программе. Это и роли, и профили доступа, и дополнительные права и т.д.
Рассмотрим механизм настройки прав доступа с помощью ролей как в режиме 1С:Предприятие, так и в режиме конфигуратора.
Для разграничения прав доступа самым простым инструментом в руках разработчика является объект "Роль", позволяющий определить доступ к объектам конфигуарции на некторые действия. Роли также могут использоваться для сложных условий проверки прав доступа с помощью такого метода как РольДоступна("ИмяРоли"). Подробнее о ролях Вы можете прочитать здесь, сегодня же рассмотрим вопрос работы с ролями в пользовательском режиме, а также некоторые моменты в реализации управления доступом с помощью ролей на уровне конфигуратора.
Откроем любой элемент справочника "Пользователи". Справа мы увидим список ролей, которые присвоены данному пользователю.
Нажав на кнопку "Изменить" мы можем изменить состав присвоенных ролей.
Все изменения ролей выполняются для соответствующего пользователя информационной базы, к которому привязан редактируемый элемент справочника.
Следовательно, список ролей, доступных для пользователя, хранится в настройках пользователя информационной базы. Изменяя права доступа с помощью ролей нужно четко понимать "что?" и "как?" отрывает для доступа та или иная роль. Общее назначение можно узнать уже из названия роли, а тонкости - либо посмотреть в документации к конфигурации, либо открыть конфигуратор.
Режим конфигуратора
Функционал управления пользователями в УПП частично взят из конфигурации "Библиотека стандартных подсистем". Рассмотрим как реализовано подбор ролей для пользователя, их разделение по подсистемам (см. первый скриншот к статье), а также считывание и запись ролей для отдельного пользователя. Начнем с подбора.
Список ролей читается из метаданных конфигурации. При открытии формы подбора ролей галочками отмечаются те роли, которые доступны для текущего пользователя информационной базы. Разделение по подсистемам выполняется на основе данных общего макета.
На первом скриншоте к статье мы видим группировку для двух подсистем: "Производство" и "Торговля и склад". Есть и другие подсистемы. Если мы обратимся к общему макету "ОписаниеРолейКонфигурации", то увидим следующее:
В верхней части макета, в области "ГруппыРолей" перечисляются список групп, на которые группируются роли конфигурации. В нижней части, в области "ОписаниеРолей" для каждой роли конфигурации устанавливается группа роли. Именно на основе макета осуществляется заполнения списка ролей и их групп в подборе.
Получение списка ролей пользователя осуществляется из данных пользователя информационной базы, привязанного к элементу справочника "Пользователи" по GUID'у. GUID пользователя инф. базы содержится в реквизита справочника "ИдентификаторПользователяИБ".
Считать список доступных ролей пользователя информационной базы можно получить пользователя:
Функция ПолучитьПользователяИБ(ЗначениеКопирования = Неопределено)
Если ЗначениеЗаполнено(ЗначениеКопирования) Тогда
ПользовательИБ = ПользователиИнформационнойБазы.
НайтиПоУникальномуИдентификатору(ЗначениеКопирования.ИдентификаторПользователяИБ);
Иначе
Если РассогласованиеДанных = 3 Тогда
ПользовательИБ = ПользователиИнформационнойБазы.
НайтиПоУникальномуИдентификатору(Объект.ИдентификаторПользователяИБ);
Иначе
ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоИмени(СокрЛП(Объект.Код));
КонецЕсли;
КонецЕсли;
Возврат ПользовательИБ;
КонецФункции
После получения пользователя инф. базы по сохраненному GUIDу мы можем обойти роли пользователя:
Процедура ЗаполнитьСписокРолейПользователяИБ(Знач ПользовательИБ = Неопределено)
// Если у пользователя нет административных прав,
// то на форме не отображаются свойства пользователя ИБ
Если НЕ ПравоАдминистрирования Тогда
Возврат;
КонецЕсли;
РолиНеСоответствуютПрофилю = Ложь;
НетРолиПользователь = Ложь;
РолиПользователя.Очистить();
Если ПользовательИБ = Неопределено Тогда
ПользовательИБ = ПолучитьПользователяИБ();
КонецЕсли;
КоллекцияРолейПрофиля = ПолучитьСписокРолейПрофиля(Объект.ПрофильПолномочийПользователя);
Если ПользовательИБ = Неопределено Тогда
Возврат;
КонецЕсли;
// При заполнении ролей переменная будет расчитана заново
НетРолиПользователь = Истина;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Есть пользователь ИБ. Покажем список его ролей !!!!!!
Для Каждого РольПользователя ИЗ ПользовательИБ.Роли Цикл
Если РольПользователя.Имя = "Пользователь" Тогда
// Роль "Пользователь" не показываем в списке
// Предполагается, что она есть у всех пользователей
НетРолиПользователь = Ложь;
Продолжить;
КонецЕсли;
СтрокаРоли = РолиПользователя.Добавить();
СтрокаРоли.ИмяРоли = РольПользователя.Имя;
СтрокаРоли.ПредставлениеРоли = РольПользователя.Синоним;
СтрокаРоли.Пометка = Истина;
// Определим есть ли роль в профиле
// Если ее нет, то в списке она будет выделена цветом
Если КоллекцияРолейПрофиля Неопределено Тогда
Если КоллекцияРолейПрофиля.Найти(СтрокаРоли.ИмяРоли, "Имя") Неопределено Тогда
СтрокаРоли.ПрофильСодержитРоль = Истина;
Иначе
РолиНеСоответствуютПрофилю = Истина;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Именно эта функция заполняет список ролей доступных для пользователя. Записать же состав ролей пользователя можно следующим образом:
Процедура ЗаписатьПользователяИБ(Отказ, ТекущийОбъект)
Перем СообщениеОбОшибке;
Если ПравоАдминистрирования Тогда
СообщениеОбОшибке = "";
Отказ = Ложь;
ДанныеПользователяИБ = ПодготовитьДанныеПользователяИБДляЗаписи();
// !!!!!!!!!!!! Готовим списко ролей для записи состава ролей пользователя ИБ !!!!!!!!!
СписокРолей = Новый ТаблицаЗначений;
СписокРолей.Колонки.Добавить("ИмяРоли");
СписокРолей.Колонки.Добавить("Пометка");
Если Объект.ПрофильПолномочийПользователя.Пустая() Тогда
Для Каждого ЭлементКоллекции Из РолиПользователя Цикл
НоваяСтрока = СписокРолей.Добавить();
НоваяСтрока.ИмяРоли = ЭлементКоллекции.ИмяРоли;
НоваяСтрока.Пометка = Истина;
КонецЦикла;
Иначе
КоллекцияРолей = ПолучитьСписокРолейПрофиля(Объект.ПрофильПолномочийПользователя);
Для Каждого ЭлементКоллекции Из КоллекцияРолей Цикл
НоваяСтрока = СписокРолей.Добавить();
НоваяСтрока.ИмяРоли = ЭлементКоллекции.Имя;
НоваяСтрока.Пометка = Истина;
КонецЦикла;
КонецЕсли;
// Роль "Пользователь" добавляем автоматически
Если СписокРолей.Найти("Пользователь", "ИмяРоли") = Неопределено Тогда
НоваяСтрока = СписокРолей.Добавить();
НоваяСтрока.ИмяРоли = "Пользователь";
НоваяСтрока.Пометка = Истина;
КонецЕсли;
Если Не Отказ Тогда
// Идентификатор не валидный, пытаемся идентифицироваться по имени
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Записываем пользователя инф. базы с текущим составом ролей
// с помощью функции "ЗаписатьПользователяИБ"
// стандартной подсистемы "Пользователи"
// В 5 параметр передается таб. значений состава ролей для записи.
Если РассогласованиеДанных = 1 ИЛИ РассогласованиеДанных = 2 Тогда
Идентификатор = УправлениеПользователямиСервер.ЗаписатьПользователяИБ(
СокрЛП(ТекущийОбъект.Ссылка.ПолучитьОбъект().Код),
ДанныеПользователяИБ,
СообщениеОбОшибке,
СписокРолей);
Иначе
Идентификатор = УправлениеПользователямиСервер.ЗаписатьПользователяИБ(
ТекущийОбъект.ИдентификаторПользователяИБ,
ДанныеПользователяИБ,
СообщениеОбОшибке,
СписокРолей);
КонецЕсли;
Если Идентификатор = Неопределено Тогда
Отказ = Истина;
Иначе
ТекущийОбъект.ИдентификаторПользователяИБ = Идентификатор;
КонецЕсли;
КонецЕсли;
Если Отказ Тогда
Если ЗначениеЗаполнено(СообщениеОбОшибке) Тогда
ТекстСообщенияОбОшибке = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
НСтр("ru = 'Ошибка записи пользователя ИБ: %1'"),
СообщениеОбОшибке );
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстСообщенияОбОшибке);
Иначе
ОбщегоНазначенияКлиентСервер.
СообщитьПользователю(НСтр("ru = 'Пользователь информационной базы не был записан'"));
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Более подробно работу с пользователями Вы можете посмотреть в конфигурации "Библиотека стандартных подсистем" или найти в синтаксис-помощнике информацию по работе с пользователями информационной базы.
В некоторых ситуациях необходимо открыть доступ к информационной базе 1С:Предприятия только на чтение/просмотр для сторонних пользователей. Например, службе аудита может понадобиться посмотреть данные о деятельности предприятия. Далее рассказывается достаточно простой способ добавления таких прав в систему для конфигурации "Управление производственным предприятием" версии 1.3.
Предисловие
И так, нам нужно добавить в систему права доступа на ТОЛЬКО ПРОСМОТР информации без прав на редактирование, изменение.
Использовать стандартный функционал не представляется возможным, поскольку та же роль "Пользователь" в УПП имеет права на изменение некоторых справочников. К тому же, нужно создать права, не зависящие от дополнительных прав пользователя и достаточно простые в настройки (чтобы достаточно было поставить одну галочку).
Решение
Решение само по себе достаточно простое с несколькими нюансами. Добавим в конфигурацию новую роль "DEV_ТолькоПросмотр". В ней для каждого объекта метаданных конфигурации установим права на чтение и просмотр, получение.
Для удобства дальней разработки и последующей настройки прав для новых объектов, установим опцию "Устанавливать права для новых объектов".
Уже сейчас, добавив эту роль пользователю, мы откроем ему доступ на чтение/просмотр большинства таблиц базы данных. Однако, типовой механизм не позволяет работать в системе, если пользователю не присвоена типовая роль "Пользователь". Исправим это. В модуле обычного приложения в обработчике события "ПередНачаломРаботыСистемы" исправим проверку на доступную роль "Пользователь":
// Перед началом работы системы
//
Процедура ПередНачаломРаботыСистемы(Отказ)
Если НЕ РольДоступна("Пользователь")
И (НЕ РольДоступна("ПолныеПрава"))
// !!! Разрешаем запуск для роли "DEV_ТолькоПросмотр" !!!
И НЕ РольДоступна("DEV_ТолькоПросмотр") Тогда
Предупреждение("Вам не назначена роль ""Пользователь"". Запуск конфигурации невозможен.");
Отказ = Истина;
Возврат;
КонецЕсли;
Отказ = НЕ УправлениеПользователями.ПользовательОпределен();
// ОбновлениеВерсииИБ
Отказ = Отказ ИЛИ НЕ ОбновлениеИнформационнойБазыКлиент.ВозможноВыполнитьОбновлениеИнформационнойБазы();
// Конец ОбновлениеВерсииИБ
КонецПроцедуры
Для правильной работы конфигурации также добавим измененим условие экспортной функции "ПользователюРазрешенЗапускКонфигурации" из общего модуля "УправлениеПользователямиСервер":
Функция ПользователюРазрешенЗапускКонфигурации() Экспорт
Если НЕ РольДоступна("Пользователь")
И (НЕ РольДоступна("ПолныеПрава"))
// !!! Разрешаем запуск для роли "DEV_ТолькоПросмотр" !!!
И (НЕ РольДоступна("DEV_ТолькоПросмотр")) Тогда Возврат Ложь;
КонецЕсли;
Возврат Истина;
КонецФункции //
Последний шаг, который нужно сделать - это автоматически очищать доступные роли пользователя информационной базы, если у него установлена роль "DEV_ТолькоПросмотр". После очистки оставлять только эту роль. Напомню, что по умолчанию система всегда добавляет роль "Пользователь" в состав доступных ролей.
Очистку состава ролей будем выполнять перед записью элемента справочника "Пользователи". Вот программный код обработчика перед записью справочника:
Процедура DEV_ПередЗаписьюПользователяПередЗаписью(Источник, Отказ) Экспорт
ПользовательИБ =
ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(Источник.ИдентификаторПользователяИБ);
Если ПользовательИБ <> Неопределено Тогда
Роли = ПользовательИБ.Роли;
Если Роли.Содержит(Метаданные.Роли.DEV_ТолькоПросмотр) Тогда
Роли.Очистить();
Роли.Добавить(Метаданные.Роли.DEV_ТолькоПросмотр);
КонецЕсли;
ПользовательИБ.Записать();
КонецЕсли;
КонецПроцедуры
Не забудьте добавить в роли "DEV_ТолькоПросмотр" права на запуск программы в режиме толстоко клиента, тонкого клиента и веб-клиента, иначе пользователь просто не сможет запустить программу. По необходимости установите другие права доступа.
В режиме 1С:Предприятие
После присваивания созданной роли пользователю, он сможет просматривать любую информацию в информационной базе.
Изменить какую-либо запись справочника или отменить проведение документа пользователь не сможет.
Профили
Описание механизма профилей прав доступа, их использование, а также их устройство в конфигураторе.
Ранее мы рассмотрели механизм изменения прав доступа пользователей с помощью ролей, общие моменты его реализации в конфигураторе. Сегодня мы коснемся механизма профилей полномочий пользователей, который позволяет изменять права пользователей группами по несколько пользователей в конфигурации "Управление производственным предприятием".
Режим 1С:Предприятие
В форме элемента справочника "Пользователи" мы можем видеть реквизит "Профиль". При указании профиля доступа полномочия пользователя становятся такими же, как и настройки прав в самом профиле.
Профили хранятся в справочнике "Профили полномочий пользователей". Заполнив реквизит "Профиль" права доступа пользователя заполняются значениями из профиля. Подбор ролей, настройка дополнительных прав и т.д. становятся недоступными. Изменение прав доступа становится возможным только с помощью профиля доступа.
В профиле можно установить основной интерфейс, состав ролей и настроить дополнительные права.
Режим конфигуратора
Рассмотрим структуру метаданных справочника "Профили полномочий пользователей".
В реквизите "ОсновнойИнтерфейс" хранится имя интерфейса, открываемого по умолчанию при запуске программы для пользователя. Имя хранится в виде строки. В таблице "СостаРолей" хранится список ролей доступа, которые включаются для пользователей с данным профилем. В таблице два реквизта строкового типа. В реквизите "ИмяРоли" сохраняется имя роли в дереве метаданных, а представление - синоним роли.
Как уже говорили выше, после выбора профиля, изменять настройки прав доступа для отдельного пользователя становится невозможным. При записи элемента справочника "Пользователи" настройки прав доступа для связанного пользователя информационной базы берутся по профилю доступа.
Рассматривать принцип действия дополнительных прав мы будем в следующих статьях. Сейчас посмотрим как записываются настройки доступа пользователя ИБ в соответствии с настройкой профиля.
В обработчике "ПередЗаписьюНаСервере" выполняется запись пользователя инф. базы. Во так выглядит процедура записи:
Процедура ЗаписатьПользователяИБ(Отказ, ТекущийОбъект)
Перем СообщениеОбОшибке;
Если ПравоАдминистрирования Тогда
СообщениеОбОшибке = "";
Отказ = Ложь;
ДанныеПользователяИБ = ПодготовитьДанныеПользователяИБДляЗаписи();
СписокРолей = Новый ТаблицаЗначений;
СписокРолей.Колонки.Добавить("ИмяРоли");
СписокРолей.Колонки.Добавить("Пометка");
Если Объект.ПрофильПолномочийПользователя.Пустая() Тогда
Для Каждого ЭлементКоллекции Из РолиПользователя Цикл
НоваяСтрока = СписокРолей.Добавить();
НоваяСтрока.ИмяРоли = ЭлементКоллекции.ИмяРоли;
НоваяСтрока.Пометка = Истина;
КонецЦикла;
Иначе // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Если профиль заполнен - заполняем список ролей из профиля
КоллекцияРолей = ПолучитьСписокРолейПрофиля(Объект.ПрофильПолномочийПользователя);
Для Каждого ЭлементКоллекции Из КоллекцияРолей Цикл
НоваяСтрока = СписокРолей.Добавить();
НоваяСтрока.ИмяРоли = ЭлементКоллекции.Имя;
НоваяСтрока.Пометка = Истина;
КонецЦикла;
КонецЕсли;
// Роль "Пользователь" добавляем автоматически
Если СписокРолей.Найти("Пользователь", "ИмяРоли") = Неопределено Тогда
НоваяСтрока = СписокРолей.Добавить();
НоваяСтрока.ИмяРоли = "Пользователь";
НоваяСтрока.Пометка = Истина;
КонецЕсли;
Если Не Отказ Тогда
// Идентификатор не валидный, пытаемся идентифицироваться по имени
Если РассогласованиеДанных = 1 ИЛИ РассогласованиеДанных = 2 Тогда
Идентификатор = УправлениеПользователямиСервер.ЗаписатьПользователяИБ(
СокрЛП(ТекущийОбъект.Ссылка.ПолучитьОбъект().Код),
ДанныеПользователяИБ,
СообщениеОбОшибке,
СписокРолей);
Иначе
Идентификатор = УправлениеПользователямиСервер.ЗаписатьПользователяИБ(
ТекущийОбъект.ИдентификаторПользователяИБ,
ДанныеПользователяИБ,
СообщениеОбОшибке,
СписокРолей);
КонецЕсли;
Если Идентификатор = Неопределено Тогда
Отказ = Истина;
Иначе
ТекущийОбъект.ИдентификаторПользователяИБ = Идентификатор;
КонецЕсли;
КонецЕсли;
Если Отказ Тогда
Если ЗначениеЗаполнено(СообщениеОбОшибке) Тогда
ТекстСообщенияОбОшибке = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
НСтр("ru = 'Ошибка записи пользователя ИБ: %1'"),
СообщениеОбОшибке );
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстСообщенияОбОшибке);
Иначе
ОбщегоНазначенияКлиентСервер.
СообщитьПользователю(НСтр("ru = 'Пользователь информационной базы не был записан'"));
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Список ролей профиля соответственно получается из таб. части "СоставРолей", о которой мы говорили выше. Стоит отметить, что если роль была переименована в конфигураторе, то нужно откорректировать данные в таб. части "СоставРолей" в реквизите "ИмяРоли". Иначе будут появляться ошибки при записи профиля.
Функция получения списка ролей из профиля следующая:
Функция ПолучитьСписокРолейПрофиля(Профиль)
Если Профиль.Пустая() Тогда
Возврат Неопределено;
КонецЕсли;
Запрос = Новый Запрос;
ТекстЗапроса = "ВЫБРАТЬ
| ПрофилиПолномочийПользователейСоставРолей.ИмяРоли КАК Имя,
| ПрофилиПолномочийПользователейСоставРолей.ПредставлениеРоли КАК Синоним
|ИЗ
| Справочник.ПрофилиПолномочийПользователей.СоставРолей КАК ПрофилиПолномочийПользователейСоставРолей
|ГДЕ
| ПрофилиПолномочийПользователейСоставРолей.Ссылка = &Ссылка";
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("Ссылка", Профиль);
КоллекцияРолей = Запрос.Выполнить().Выгрузить();
Возврат КоллекцияРолей;
КонецФункции
Далее рассмотрим механизм дополнительных прав, принцип его работы как с точки зрения пользователя, так и разработчика.
Дополнительные права
В этом подразделе идет речь о механизме дополнительных прав пользователя в конфигурации "Управление производственным предприятием" как с точки зрения пользователя, так и с точки зрения разработчика.
Предисловие
Механизм дополнительных прав пользователей позволяет выполнять разграничение доступа на объекты конфигурации очень гибко, а его настройка выполняется достаточно просто в пользовательском режиме в отличии от ролей. Рассмотрим основные моменты работы с дополнительными правами в пользовательском режиме, а также реализацию механизма в конфигураторе.
Режим 1С:Предприятие
Дополнительные права настраиваются отдельно для каждого элемента справочника "Пользователи", либо для профиля полномочий пользователя.
Список дополнительных прав формируется на основе элементов плана видов характеристик "ПраваПользователей", в котором устанавливается тип значений настройки прав (в большинстве случаев "Булево"). Обычно в типовых конфигурациях уже имеется набор предопределенных элементов дополнительных прав. Дополнительные права можно добавлять в режиме 1С:Предприятие.
Каждое дополнительное право ограничивает доступ на определенные действия, о которых можно догадаться либо по названию доп. права, либо в документации к конфигурации.
Режим конфигуратора
Как уже было сказано, дополнительные права создаются в виде элементов плана видов характеристик. Контроль же прав и все необходимые действия для проверки прав выполняются программным кодом. Настройки прав для каждого пользователя сохраняются в регистре сведений "ЗначенияДополнительныхПравПользователя", имеющего следующую структуру метаданных:
Измерение "Пользователь" хранит ссылку на элемент справочника "Пользователи", а измерение "Право" ссылку на элемент плана видов характеристик "ПраваПользователей". Тип значений ресурса "Значение" определяется доступным типом ПВХ и его элементом (в измерении "Право").
Получить значение права можно с помощью запроса к регистру сведений. Вот так выглядит код типовой функции для проверки прав:
// Функция возвращает значение дополнительного права профиля пользователя
//
// Параметры:
// Право - право, наличие которого проверяется (Тип ПВХ.ПраваПользователей)
// ЗначениеПоУмолчанию - значение по умолчанию для передаваемого права (возвращается в случае
// отсутствия значений в регистре сведений). Тип - булево
//
// Возвращаемое значение: массив значений права
//
Функция ПолучитьЗначениеПраваПользователя(Право, ЗначениеПоУмолчанию) Экспорт
КэшДополнительныхПрав = глЗначениеПеременной("ЗначенияДополнительныхПравПользователя");
МассивЗначенийПрава = КэшДополнительныхПрав[Право];
Если МассивЗначенийПрава = Неопределено Тогда
МассивЗначенийПрава = Новый Массив;
Если РольДоступна("ПолныеПрава") Тогда
ЗначениеПрава = ПолучитьЗначениеРазрешенногоПрава(Право);
МассивЗначенийПрава.Добавить(ЗначениеПрава);
Иначе
ОбъектПрав = глЗначениеПеременной("ПрофильПолномочийПользователя");
Если ЗначениеЗаполнено(ОбъектПрав) Тогда
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ОбъектПрав", ОбъектПрав);
Запрос.УстановитьПараметр("ПравоПользователя", Право);
Запрос.Текст = "ВЫБРАТЬ РАЗРЕШЕННЫЕ
| РегистрЗначениеПрав.Значение КАК Значение
|ИЗ
| РегистрСведений.ЗначенияДополнительныхПравПользователя КАК РегистрЗначениеПрав
|ГДЕ
| РегистрЗначениеПрав.Право = &ПравоПользователя
| И РегистрЗначениеПрав.Пользователь = &ОбъектПрав";
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
ЗначениеПрава = Выборка.Значение;
Иначе
ЗначениеПрава = ЗначениеПоУмолчанию;
КонецЕсли;
МассивЗначенийПрава.Добавить(ЗначениеПрава);
Иначе
ТекущийПользователь = глЗначениеПеременной("глТекущийПользователь");
МассивЗначенийПрава = ПрочитатьЗначениеПраваПользователя(Право, ЗначениеПоУмолчанию,
ТекущийПользователь);
КонецЕсли;
КонецЕсли;
КэшДополнительныхПрав[Право] = МассивЗначенийПрава;
#Если Сервер Тогда
глЗначениеПеременнойУстановить("ЗначенияДополнительныхПравПользователя",
КэшДополнительныхПрав, Истина);
#КонецЕсли
КонецЕсли;
Возврат МассивЗначенийПрава;
КонецФункции
Функция выполняет запрос к регистру сведений "ЗначенияДополнительныхПравПользователя" и получает значения прав в виде массива. При повторном получении прав значение получается из кэша. Поэтому после изменения значения доп. права, чтобы новые настройки вступили в силу, необходимо перезапустить пользовательский сеанс.
Настройки пользователей
Конфигурация "Управление производственным предприятием" релиз 1.3 включает в себя механизм настроек пользователей, с помощью которого можно устанавливать параметры автозаполнения форм, настройки в документах по умолчанию и много другое.
Далее рассмотрим работы с настройками пользователей в режиме 1С:Предприятие, а также его реализацию в конфигураторе.
Режим 1С:Предприятие
Откроем окно настрое пользователя (не важно какого). Увидим примерно следующее:
Продемонстрируем влияние настроек на работу пользователя. Изменим настройку "Запретить открытие нескольких сеансов" в значение ИСТИНА. Затем, не закрывая текущий сеанс, попытаемся войти в программу под той же учетной записью. Система нам выдаст предупреждение и не закроет сеанс:
Если мы вернем настройку в прежнее состояние, то запуск нескольких сеансов станет возможным.
Как Вы могли заметить, список доступным настроек пользователей достаточно обширный. Подробнее о назначении каждой из них Вы можете прочитать во встроеной справке конфигурации "Управление производственным предприятием", за исключением тех случаев, когда настройки были добавлены пользователями/администратором и не являются предопределенными.
Режим конфигуратора
Механизм настроек пользователей имеет достаточно простую реализацию. Используются всего два объекта в дереве конфигурации:
- План видов характеристик "НастройкиПользователей".
- Регистр сведений "НастройкиПользователей".
План видов характеристик определяет тип значения характеристики, сохраняемой в регистре сведений "НастройкиПользователей". Вот список некоторых доступных типов значений, установленный в типовой конфигурации:
Для элемента "Запретить открытие нескольких сеансов" плана видов характеристик "Настройки пользователей" установлен тип "Булево". При запуске программы производится проверка включения этой опции. Если значение для настройки по текущему пользователю установлено в ИСТИНА, тогда выполняется проверка наличия запущенных сеаносв этим пользователем.
Вот часть кода проверки из модуля обычного приложения события "ПриНачалеРаботыСистемы":
// При начале работы системы
//
Процедура ПриНачалеРаботыСистемы()
/// .............................
// Получаем значение настройки
ЗапретитьОткрытиеНесколькихСеансов = УправлениеПользователями.ПолучитьЗначениеПоУмолчанию(
глЗначениеПеременной("глТекущийПользователь"),
"ЗапретитьОткрытиеНесколькихСеансов"
);
Если ЗапретитьОткрытиеНесколькихСеансов Тогда
ТекущийНомерСоединения = НомерСоединенияИнформационнойБазы();
УникальныйИдентификаторПользователя =
ПользователиИнформационнойБазы.ТекущийПользователь().УникальныйИдентификатор;
// Ищем текущего пользователия ИБ в уже подключенных сеансах
МассивСоединений = ПолучитьСоединенияИнформационнойБазы();
Для Каждого ТекСоединение Из МассивСоединений Цикл
Если (ТекСоединение.ИмяПриложения = "1CV8")
И (НЕ ТекСоединение.НомерСоединения = ТекущийНомерСоединения)
И (НЕ ТекСоединение.Пользователь = неопределено)
И (ТекСоединение.Пользователь.УникальныйИдентификатор =
УникальныйИдентификаторПользователя) Тогда
Предупреждение("Пользователем с таким именем уже выполнен вход в систему");
ЗавершитьРаботуСистемы(Ложь);
Возврат;
КонецЕсли;
КонецЦикла;
КонецЕсли;
// .....................................
КонецПроцедуры
Самый интересный момент - это процедура "ПолучитьЗначениеПоУмолчанию". Первым параметром в нее передается текущий пользователь (ссылка на элемент справочника "Пользователи"), вторым - имя элемента плана видов характеристик "Настройки пользователей", для которого нужно получить значение. Вот так выглядит код обработчика этой функции:
// Функция возвращает значение по умолчанию для передаваемого пользователя и настройки.
//
// Параметры:
// Пользователь - текущий пользователь программы
// Настройка - признак, для которого возвращается значение по умолчанию
//
// Возвращаемое значение:
// Значение по умолчанию для настройки.
//
Функция ПолучитьЗначениеПоУмолчанию(Пользователь, Настройка) Экспорт
Если Пользователь = глЗначениеПеременной("глТекущийПользователь") Тогда
НастройкаСсылка = ПланыВидовХарактеристик.НастройкиПользователей[Настройка];
КэшНастроекПользователей = глЗначениеПеременной("ЗначенияНастроекПользователей");
ЗначениеНастройки = КэшНастроекПользователей[НастройкаСсылка];
Если ЗначениеНастройки = Неопределено Тогда
ЗначениеНастройки = ПолучитьЗначениеПоУмолчаниюПользователя(Пользователь, Настройка);
КэшНастроекПользователей[НастройкаСсылка] = ЗначениеНастройки;
#Если НЕ Клиент И НЕ ВнешнееСоединение Тогда
// На сервере cохраним значение в КЭШе
// В других случаях значение получено "по ссылке", сохранять не требуется
глЗначениеПеременнойУстановить("ЗначенияНастроекПользователей",
КэшНастроекПользователей, Истина);
#КонецЕсли
КонецЕсли;
Возврат ЗначениеНастройки;
КонецЕсли;
Возврат ПолучитьЗначениеПоУмолчаниюПользователя(Пользователь, Настройка);
КонецФункции // ПолучитьЗначениеПоУмолчанию()
Здесь, если получаем настройку для текущего пользователя, то ее значение пытаемся получить из кэша. В остальных случаях непосредственно из регистра сведений "НастройкиПользователей".
Таким же образом выполняется обработка настроек пользователей в других процедурах и функциях конфигурации. Получаем значение настройки и выполняем действия в зависимости от условий.
Ограничения на уровне записей
Далее описаны действия по настройке механизма RLS в конфигурации "Управление производственным предприятием" версии 1.3 для ограничения доступа пользователей к документа по организациям.
Общие сведения
Платформа 1С:Предприятие 8 имеет встреный механизм ограничения доступа к данным на уровне записей. Общие сведения о нем Вы можете прочитать здесь. Если кратко, то RLS позволят ограничить доступ к данным по некоторым условиям на значения полей. Например, можно ограничить доступ пользователей к документам в зависимости от значения реквизита "Организация". Некоторые пользователи будут работать с документами по организации "Управляющая компания", а остальные с организацией "Молочный завод". Как пример.
Далее в статье подробно рассмотрим шаги по настройке RLS в конфигурации "Управление производственным предприятием" для разграничения доступа пользователей по организациям.
Подготовка
Пример реализуем в демонстрационной конфигурации УПП 1.3. Создадим пользователя "Кладовщик" и добавим ему одноименную
Теперь приступим непосредственно к настройке прав доступа на уровне записей. Переключимся на интерфейс "Администрирование пользователе". В главном меню выберем "Доступ на уровне записей -> Параметры". Здесь отметим галкой "Ограничить доступ на уровне записей по видам объектов", а в списке объектов выберем "Организации".
ем самым мы включили использование RLS. Теперь нужно его настроить.
Разграничение доступа на уровне записей настраивается не отдельно для каждого пользователя или профилей полномочий. RLS настраивается для групп пользователей. Добавим новую группу пользователей, назовем ее "Кладовщики"
Состав группы справа на форме показывает список пользователей, относящихся к этой группе. Добавим в состав созданного нами ранее пользователя. Слева таблица ограничений доступа. В настройка RLS мы выбрали, что доступ будет разграничиваться только по организациям, поэтому мы видим только один вид объекта доступа. Нажмем на кнопку "Настройка доступа". Откроется обработка настройки прав доступа для текущей группы.
В список объектов доступа для группы добавим организацию "ИЧП "Предприниматель"". Вид наследования прав оставим без изменений. Право на объект доступа установим для чтения и записи. Нажмем "ОК", настройки готовы. Мы только что настроили RLS на уровне организаций.
Что видит пользователь
Запустим программу под созданным ранее пользователем и откроем справочник "Организации". Вот так будет выглядеть список для нашего пользователя и для пользователя с полными правами:
Как мы видим, пользователь кладовщик видит только одну организацию, для которой мы открыли доступ на чтение. Тоже относится и к документам, например, поступления товаров и услуг.
Таким образом, пользователь не только не увидит организации, доступ на которые не установлен для него, но и не сможет прочитать/записать документы и другие объекты в информационной базе, на которые установлены права в роля на реквизит "Организация".
Мы рассмотрели простейший пример настройки RLS.
Далее описывается общая схема реализации механизма ограничения доступа на уровне записей (RLS) в конфигурации "Управление производственным предприятием" версии 1.3. Перечислены основные объекты конфигурации, используемые в RLS, а также их связь между собой.
Предисловие
Ранее мы рассмотрели простой пример настройки прав доступа на уровне записей по организациям, описав настройку по шагам.
Далее мы рассмотрим общую схему реализации RLS в конфигурации "Управление производственным предприятием" версии 1.3 с позиции разработчика.
Используемые объекты
Для начала определим состав объектов конфигурации, используемые для реализации этого механизма, и дадим краткое описание их назначения.
Начнем с самого главного - роли. Именно в ролях описываются правила ограничения на уровне записей в виде текстов запросов, похожих на стандартный синтаксис языка запросов платформы, но с некоторыми отличиями. Подробно останавливаться на задании условий RLS в ролях не буду. Отмечу лишь, что в роли "Кладовщик", используемой нами выше в примерах, условие на доступ (право "Чтение") по реквизиту "Организация" для документов выглядит примерно таким образом:
#Если &ИспользоватьОграничениеПоОрганизации #Тогда
ТекущаяТаблица
ИЗ
#ТекущаяТаблица КАК ТекущаяТаблица
ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ
СоставГруппы.Ссылка КАК ГруппаПользователей
ИЗ
Справочник.ГруппыПользователей.ПользователиГруппы КАК СоставГруппы
ГДЕ
СоставГруппы.Пользователь = &ТекущийПользователь) КАК ГруппыПользователей
ПО (ИСТИНА)
ГДЕ
НЕ ГруппыПользователей.ГруппаПользователей ЕСТЬ NULL
И
(НЕ 1 В
(ВЫБРАТЬ ПЕРВЫЕ 1
1
ИЗ
РегистрСведений.НазначениеВидовОбъектовДоступа
КАК НазначениеВидовОбъектовДоступа
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПравДоступаПользователей
КАК НастройкиПравДоступаПользователей
ПО
НастройкиПравДоступаПользователей.ОбъектДоступа =
ТекущаяТаблица.#Параметр(1) И НастройкиПравДоступаПользователей.ВидОбъектаДоступа
= НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
И НастройкиПравДоступаПользователей.ОбластьДанных
= ЗНАЧЕНИЕ(Перечисление.ОбластиДанныхОбъектовДоступа.ПустаяСсылка)
И НастройкиПравДоступаПользователей.Пользователь
= ГруппыПользователей.ГруппаПользователей
ГДЕ
НазначениеВидовОбъектовДоступа.ГруппаПользователей
= ГруппыПользователей.ГруппаПользователей
И НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
В (ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации))
И НастройкиПравДоступаПользователей.ОбъектДоступа ЕСТЬ NULL))
#КонецЕсли
Обратите внимание на конструкцию "#ИспользоватьОграничениеПоОрганизации". Не трудно догадаться, что при установке флага в ИСТИНА RLS по организациям начинает функционировать. Значение этого флага платформа берет из одноименного параметра сеанса. Вот список параметров сеанса, используемых RLS:
Параметр "ИспользоватьОграниченияПравДоступаНаУровнеЗаписей" используется для определения факта использования RLS в базе данных. Обратимся к обработчику инициализации перечисленных параметров сеанса:
Процедура УстановитьПараметрыМеханизмаОграниченияПравДоступа() Экспорт
// Получаем флаг использования ограничений на уровне записей
ПараметрыСеанса.ИспользоватьОграниченияПравДоступаНаУровнеЗаписей
= Константы.ИспользоватьОграниченияПравДоступаНаУровнеЗаписей.Получить();
// Инициализация параметров сеанса
Если ПараметрыСеанса.ИспользоватьОграниченияПравДоступаНаУровнеЗаписей Тогда
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ВидыОбъектовДоступа.Ссылка,
| МАКСИМУМ(ВЫБОР
| КОГДА ПользователиГруппы.Ссылка ЕСТЬ NULL
| ТОГДАИСТИНА
| ИНАЧЕВЫБОР
| КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
| ЕСТЬ NULL
| ТОГДАЛОЖЬ
| ИНАЧЕИСТИНА КОНЕЦ
| КОНЕЦ)КАК ЕстьОграничение
|ИЗ
| Перечисление.ВидыОбъектовДоступа КАК ВидыОбъектовДоступа
| ЛЕВОЕ СОЕДИНЕНИЕ
| Справочник.ГруппыПользователей.ПользователиГруппы
| КАК ПользователиГруппы
| ПО (ПользователиГруппы.Пользователь =&ТекущийПОльзователь)
| ЛЕВОЕ СОЕДИНЕНИЕ
| РегистрСведений.НазначениеВидовОбъектовДоступа
| КАК НазначениеВидовОбъектовДоступа
| ПО (НазначениеВидовОбъектовДоступа.ГруппаПользователей
| =ПользователиГруппы.Ссылка)
| И(НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа =
| ВидыОбъектовДоступа.Ссылка)
|
|СГРУППИРОВАТЬ ПО
| ВидыОбъектовДоступа.Ссылка";
Запрос.УстановитьПараметр("ТекущийПользователь",
ПараметрыСеанса.ТекущийПользователь);
Выборка = Запрос.Выполнить().Выбрать();
ВидыОбъектовДоступаЗначения =
Метаданные.Перечисления.ВидыОбъектовДоступа.ЗначенияПеречисления;
Пока Выборка.Следующий() Цикл
ПараметрыСеанса["ИспользоватьОграничениеПо" +
ВидыОбъектовДоступаЗначения.Получить(
Перечисления.ВидыОбъектовДоступа.Индекс(Выборка.Ссылка)
).Имя] = Выборка.ЕстьОграничение;
КонецЦикла;
Иначе
Для Каждого ВидОбъектаДоступа Из
Метаданные.Перечисления.ВидыОбъектовДоступа.ЗначенияПеречисления Цикл
ПараметрыСеанса["ИспользоватьОграничениеПо" +
ВидОбъектаДоступа.Имя] = Ложь;
КонецЦикла;
КонецЕсли;
УстановитьПараметрСеансаТекущиеУчетныеЗаписиНалогоплательщика();
КонецПроцедуры
Параметр сеанса "ИспользоватьОграниченияПравДоступаНаУровнеЗаписей" устанавливается из одноименной константы. Значения остальных параметров сеанса устанавливаются в соответствии со значениями, установленными для группы текущего пользователя в регистре сведений "НазначениеВидовОбъектовДоступа".
Список всех видов объектов доступа представлен в перечислении "ВидыОбъектовДоступа".
Настройки прав доступа непосредственно для каждой группы пользователей хранятся в регистре сведений "НастройкиПравДоступаПользователей", в котором указывается "ВидДоступа", "ОбъектДоступа", "ГруппаПользователей" и другие настройки, определяющие возможность выполнять чтение | запись данных. Изменение записей регистра осуществляется обработкой "Настройка прав доступа", которую мы использовали в предыдущей статье.
Использование RLS и, в частности, того или иного вида объекта доступа настраивается с помощью обработки "НастройкиПрограммы" и ее формы "ПараметрыДоступаНаУровнеЗаписей". Вы могли видеть ее в предыдущей статье.
Картина целиком
Возможно, получилось немного сумбурно. В качестве вывода представляю схему взаимосвязи все описанных выше объектов.
На схеме также представлены дополнительные объекты, необходимые для реализации RLS. Для элементв перечисления "ВидыОбъектовДоступа" добавил соответствующие справочники. В конечнм счете показано какие данные используются для наложения уловий на запросы к базе в ролях.
Далее более подробно рассмотрим синтаксис языка запросов RLS в ролях.
Далее рассмотрен шаблон ограничений доступа на уровне записей для справочника "Организации". Проанализировано его влияние на запрос получения данных из справочника. Рассматриваемый шаблон используется в типовой конфигурации "Управление производственным предприятием" версии 1.3.
Шаблон
Текст шаблона будем анализировать для роли "Пользователь", которая автоматически добавляется для всех пользователей информационной базы. Откроем ее в конфигураторе и перейдем к ее настройкам для справочника "Организации". Мы увидим следующее для права "Чтение":
Перейдя на вкладку "Шаблоны ограничений" и открыв текст шаблона ограничений мы увидим такой шаблон:
// Получаем признак использования ограничений на
// уровне записей для справочника "Организации"
// из одноименного параметра сеанса
#Если &ИспользоватьОграничениеПоОрганизации #Тогда
ТекущаяТаблица
ИЗ
// "#ТекущаяТаблица" - это имя таблицы, к которой выполняется запрос
// В нашем примере это таблица справочника "Организации"
#ТекущаяТаблица КАК ТекущаяТаблица
ЛЕВОЕ СОЕДИНЕНИЕ
// К текущей таблице присоединяется результат вложенного запроса,
// получающий список групп пользователей, в которых
// состоит текущий пользователь. В результате к элементу спр.
// "Организации" присоединяются левым соединением все группы
// текущего пользователя
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
(ВЫБРАТЬ РАЗЛИЧНЫЕ
СоставГруппы.Ссылка КАК ГруппаПользователей
ИЗ
Справочник.ГруппыПользователей.ПользователиГруппы
КАК СоставГруппы
ГДЕ // ТекущийПользователь - ссылка на элемент спр. "Пользователи",
// хранящегося в параметре сеанса "ТекущийПользователь"
СоставГруппы.Пользователь = &ТекущийПользователь)
КАК ГруппыПользователей
ПО (ИСТИНА)
// -------------------------------------------------------------
ГДЕ // Накладываем условия на проверку доступа к текущей организации
// Первое условие - для текущего пользователя должна быть установлена
// хотя бы одна группа пользователей, чтобы дать ему права
НЕ ГруппыПользователей.ГруппаПользователей ЕСТЬ NULL
// Второе условие - хотя бы для одной группы пользователей, в которую
// входит текущий пользователь, должны быть установлены права на
// чтение в регистре сведений "Настройки прав доступа пользователей"
И
(НЕ 1 В
(ВЫБРАТЬ ПЕРВЫЕ 1
1
ИЗ
// В таблице "НазначениеВидовОбъектовДоступа" получаем список
// объектов доступа, на которые установлены настрйки прав для
// полученного списка групп пользователей (см. выше)
РегистрСведений.НазначениеВидовОбъектовДоступа
КАК НазначениеВидовОбъектовДоступа
// Присоединяем таблицу "НастройкиПравДоступаПользователей",
// в которой хранятся настройки доступа RLS
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПравДоступаПользователей
КАК НастройкиПравДоступаПользователей
ПО
НастройкиПравДоступаПользователей.ОбъектДоступа
// "#Параметр(1)" - это выражение получает значение параметра
// "Ссылка", переданного в шаблон ограничения доступа. В
// нашем случае это ссылка на текущий элемент спр. "Организации"
// (см. скриншот выше)
= ТекущаяТаблица.#Параметр(1)
И НастройкиПравДоступаПользователей.ВидОбъектаДоступа
= НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
И НастройкиПравДоступаПользователей.ОбластьДанных
= ЗНАЧЕНИЕ(Перечисление.ОбластиДанныхОбъектовДоступа.ПустаяСсылка)
И НастройкиПравДоступаПользователей.Пользователь
= ГруппыПользователей.ГруппаПользователей
ГДЕ
НазначениеВидовОбъектовДоступа.ГруппаПользователей
= ГруппыПользователей.ГруппаПользователей
И НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
В (ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации))
И НастройкиПравДоступаПользователей.ОбъектДоступа ЕСТЬ NULL))
#КонецЕсли
Шаблон достаточно подробно закомментировал.
Влияние на запрос
А теперь сравним изменение запроса к базе данных с учетом влияния на него ограничений на уровне записей. Пример будет основываться на рассмотренном выше тексте шаблона. И так, вот таким будет первоначальный запрос к справочнику "Организации":
"ВЫБРАТЬ
| *
|ИЗ
| Справочник.Организации КАК Организации"
А вот таким он будет с учетом ограничений на уровне записей:
"ВЫБРАТЬ
| *
|ИЗ
| Справочник.Организации КАК Организация
| ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ
| СоставГруппы.Ссылка КАК ГруппаПользователей
| ИЗ
| Справочник.ГруппыПользователей.ПользователиГруппы КАК СоставГруппы
| ГДЕ
| СоставГруппы.Пользователь = &ТекущийПользователь) КАК ГруппыПользователей
| ПО (ИСТИНА)
|ГДЕ
| НЕ ГруппыПользователей.ГруппаПользователей ЕСТЬ NULL
| И НЕ 1 В
| (ВЫБРАТЬ ПЕРВЫЕ 1
| 1
| ИЗ
| РегистрСведений.НазначениеВидовОбъектовДоступа КАК НазначениеВидовОбъектовДоступа ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПравДоступаПользователей КАК НастройкиПравДоступаПользователей
| ПО
| НастройкиПравДоступаПользователей.ОбъектДоступа = &СсылкаНаОрганизацию
| И НастройкиПравДоступаПользователей.ВидОбъектаДоступа = НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
| И НастройкиПравДоступаПользователей.ОбластьДанных = ЗНАЧЕНИЕ(Перечисление.ОбластиДанныхОбъектовДоступа.ПустаяСсылка)
| И НастройкиПравДоступаПользователей.Пользователь = ГруппыПользователей.ГруппаПользователей
| ГДЕ
| НазначениеВидовОбъектовДоступа.ГруппаПользователей = ГруппыПользователей.ГруппаПользователей
| И НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа В (ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации))
| И НастройкиПравДоступаПользователей.ОбъектДоступа ЕСТЬ NULL )
| И Организация.Ссылка = &СсылкаНаОрганизацию"
В зарос добавлен параметр "СсылкаНаОрганизацию", чтобы правильно смоделировать работу запроса. В нашем случае в параметр передается ссылка на организацию, к которой проверяется доступ на чтение.
На этом все.
Далее идет речь о настройке прав доступа на уровне записей для справочника "Контрагенты". В предыдущих статьях в примерах мы говорили о настройке доступа к справочнику "Организации". Для справочника "Контрагенты" есть некоторые нюансы, которые и будут описаны. Пример будет рассматриваться для конфигурации УПП 1.3.
Предисловие
Ранее мы рассматривали сквозной пример настройки ограничений на уровне записей (RLS) по организациями в УПП 1.3. Сегодня мы рассмотрим еще небольшой пример настройки RLS по контрагентам, поскольку ход действий будет немного отличаться от процесса настройки доступа по организациям.
И так, приступим!
Настройки прав
Допустим, нам нужно для пользователя из отдела оптовых продаж "Бильданова Алексея Николаевича" добавить права на работу с некоторым списком контрагентов. Пусть это будут некоторые VIP-клиенты нашей организации (см. скриншот выше).
Для решения поставленной задачи практически идеально подойдет механизм разграничения прав на уровне записей (RLS). Зайдя в программу под администратором, включим интерфейс администрирования пользователей и в меню "Доступ на уровне записей" перейдем в параметры.
Здесь нам нужно установить галку "Контрагенты". Тем самым мы включили разграничение доступа на уровне записей для вида объектов "Контрагенты". На скриншоте также включено разграничение прав по организациям, которое мы настраивали в предыдущей статье. Как уже упомяналось ранее в статьях, работа RLS в УПП основывается на группах пользователей. Поэтому создадим новую группу пользователей "Доступ к VIP-клиентам" и включим в ее состав пользователя "Бильданова".
Прежде чем приступать к настройка прав доступа для созданной группы пользователей, создадим группу доступа контрагентов, к которой в дальнейшем и будут привязываться настройки RLS.
Теперь перейдем к настройке прав для группы пользователей. Установим следующие параметры:
В соответствии с установленными настройками, пользователи, входящие в группу пользователей "Доступ к VIP-клиентам", могут редактировать как самих контрагентов, так и их дополнительную информацию (адреса, свойства и т.д.), НО ТОЛЬКО ВХОДЯЩИХ В ГРУППУ доступа контрагентов "VIP-клиенты".
Настройка прав доступа к контрагентам на этом этапе практически завершена. Осталось лишь добавить VIP-клиентов в созданную группу доступа контрагентов. Для этого у всех элементов справочника "Контрагенты", находящихся в группе "VIP-покупатели" нужно установить значение реквизита "Группа доступа к контрагенту" в соответсвии с созданной ранее группой доступа:
На этом все, посмотрим результат.
Результат
Запустим программу в режиме 1С:Предприятия под пользователем "Бильданов". Открыв справочник "Контрагенты", нам будут доступны только те контрагенты, которые находятся в группе доступа контрагентов "VIP-клиенты".
Особенностью организации доступа на уровне записей для контрагентов в конфигурации "Управление производственным предприятием" является то, что ограничения не действуют на группу справочника "Контрагенты".
Особенностью, отличающую настройку RLS для контрагентов от справочника "Организации", является использование дополнительноо звера - справочника "Группы доступа контрагентов", в то время как для организаций доступа устанавливался непосредственно на элементы справочника. Сделано так для удобства настройки прав (более укрупненными блоками), а также для более оптимальной работы запросов к базе данных при использовании RLS.
Пример решения задачи по модификации правил ограничения доступа к данным на уровне записей (RLS) в конфигурации "Управление производственным предприятием" релиз 1.3. Способ оптимизации ограничений на уровне записей (RLS) на примере УПП 1.3.
Проблема
В конфигурации "Управление производственным предприятием" редакции 1.3 настроены права доступа на уровне записей для складов. Так выглядит таблица настроек доступа на склады для двух пользователей:
Пользователь |
Склад №1 |
Склад №2 |
||
Чтение |
Запись |
Чтение |
Запись |
|
Пользователь 1 |
V |
- |
V |
- |
Пользователь 2 |
V |
- |
V |
V |
То есть право на запись документов со складом "Склад №2" имеется только у пользователя "Пользователь 2". На чтение же права есть как у пользователя "Пользователь 2" так и у "Пользователь 1".
Логично предположить, что "Пользователь 2" не сможет провести документ "Перемещение товаров", где в качестве отправителя "Склад №1", а в качестве получателя "Склад №2".
Но типовые ограничения на уровне записей позволяют в этом случае записать/провести документ без лишних вопросов. Разберемся в причинах.
Текущее состояние
Обратимся к типовым шаблонам ограничений доступа, а именно к шаблону ограничений для операций "Изменение" и "Добавление". Права на эти операции настроены только для роли "Кладовщик".
На следующем листинге приведен типовой текст шаблона ограничений на уровне складов. Обратите внимание на условие для склада отправителя и склада получателя (комментарий с восклицательными знаками). Именно из-за этого условия программа позволяет записывать/проводить документ, если у пользователя есть право на запись хотя бы на один склад, а не для обоих складов как должно быть. Листинг текста 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 это не единственная ошибка с ограничениями на уровне складов. Интересное поведение можно заметить для регистра "УчетЗатратРегл" и еще множество других моментов. Работать можно, но иногда очень сильно мешают недоработка этих правил.
И напоследок модифицированные тексты условий на чтение и запись документа "Перемещение товаров" сделанные в первом решении:
#Если &ИспользоватьОграничениеПоОрганизации ИЛИ &ИспользоватьОграничениеПоПодразделения ИЛИ &ИспользоватьОграничениеПоСклады #Тогда
ТекущаяТаблица
ИЗ
#ТекущаяТаблица КАК ТекущаяТаблица
ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ
СоставГруппы.Ссылка КАК ГруппаПользователей
ИЗ
Справочник.ГруппыПользователей.ПользователиГруппы КАК СоставГруппы
ГДЕ
СоставГруппы.Пользователь = &ТекущийПользователь) КАК ГруппыПользователей
ПО (ИСТИНА)
ГДЕ
НЕ ГруппыПользователей.ГруппаПользователей ЕСТЬ NULL
И
(НЕ 1 В
(ВЫБРАТЬ ПЕРВЫЕ 1
1
ИЗ
РегистрСведений.НазначениеВидовОбъектовДоступа КАК НазначениеВидовОбъектовДоступа
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПравДоступаПользователей КАК НастройкиПравДоступаПользователей
ПО
ВЫБОР
#Если &ИспользоватьОграничениеПоОрганизации #Тогда
КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации)
ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.Организация
#КонецЕсли
#Если &ИспользоватьОграничениеПоПодразделения #Тогда
КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Подразделения)
ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.Подразделение
#КонецЕсли
#Если &ИспользоватьОграничениеПоСклады #Тогда
КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
// Проверка доступа на запись для склада отправителя
ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.СкладОтправитель
#КонецЕсли
КОНЕЦ
И НастройкиПравДоступаПользователей.ВидОбъектаДоступа = НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
И НастройкиПравДоступаПользователей.ОбластьДанных = ЗНАЧЕНИЕ(Перечисление.ОбластиДанныхОбъектовДоступа.ПустаяСсылка)
И НастройкиПравДоступаПользователей.Пользователь = ГруппыПользователей.ГруппаПользователей
ГДЕ
НазначениеВидовОбъектовДоступа.ГруппаПользователей = ГруппыПользователей.ГруппаПользователей
И НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа В (
ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.ПустаяСсылка)
#Если &ИспользоватьОграничениеПоОрганизации #Тогда
, ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации)
#КонецЕсли
#Если &ИспользоватьОграничениеПоПодразделения #Тогда
, ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Подразделения)
#КонецЕсли
#Если &ИспользоватьОграничениеПоСклады #Тогда
, ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
#КонецЕсли
)
И НастройкиПравДоступаПользователей.ОбъектДоступа ЕСТЬ NULL))
// Если включены ограничения по складам, то добавляем проверку доступа на запись для склада получателя
// Для проверки приходится формировать доп. запрос к регистрам назначения объектов доступа
// (!!!) Из-за доп. запроса решение является не самым оптимальным, создавая доп. нагрузку на сервер СУБД
#Если &ИспользоватьОграничениеПоСклады #Тогда
И (НЕ 1 В
(ВЫБРАТЬ ПЕРВЫЕ 1
1
ИЗ
РегистрСведений.НазначениеВидовОбъектовДоступа КАК НазначениеВидовОбъектовДоступа
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПравДоступаПользователей КАК НастройкиПравДоступаПользователей
ПО
ВЫБОР
КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
// Проверка доступа на запись по складу получателю
ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.СкладПолучатель
КОНЕЦ
И НастройкиПравДоступаПользователей.ВидОбъектаДоступа = НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
И НастройкиПравДоступаПользователей.ОбластьДанных = ЗНАЧЕНИЕ(Перечисление.ОбластиДанныхОбъектовДоступа.ПустаяСсылка)
И НастройкиПравДоступаПользователей.Пользователь = ГруппыПользователей.ГруппаПользователей
ГДЕ
НазначениеВидовОбъектовДоступа.ГруппаПользователей = ГруппыПользователей.ГруппаПользователей
И НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа В (
ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.ПустаяСсылка)
, ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
)
И НастройкиПравДоступаПользователей.ОбъектДоступа ЕСТЬ NULL))
#КонецЕсли
#КонецЕсли
#Если &ИспользоватьОграничениеПоОрганизации ИЛИ &ИспользоватьОграничениеПоПодразделения ИЛИ &ИспользоватьОграничениеПоСклады #Тогда
ТекущаяТаблица
ИЗ
#ТекущаяТаблица КАК ТекущаяТаблица
ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ
СоставГруппы.Ссылка КАК ГруппаПользователей
ИЗ
Справочник.ГруппыПользователей.ПользователиГруппы КАК СоставГруппы
ГДЕ
СоставГруппы.Пользователь = &ТекущийПользователь) КАК ГруппыПользователей
ПО (ИСТИНА)
ГДЕ
НЕ ГруппыПользователей.ГруппаПользователей ЕСТЬ NULL
И
(НЕ 1 В
(ВЫБРАТЬ ПЕРВЫЕ 1
1
ИЗ
РегистрСведений.НазначениеВидовОбъектовДоступа КАК НазначениеВидовОбъектовДоступа
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПравДоступаПользователей КАК НастройкиПравДоступаПользователей
ПО
ВЫБОР
#Если &ИспользоватьОграничениеПоОрганизации #Тогда
КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации)
ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.Организация
#КонецЕсли
#Если &ИспользоватьОграничениеПоПодразделения #Тогда
КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Подразделения)
ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.Подразделение
#КонецЕсли
#Если &ИспользоватьОграничениеПоСклады #Тогда
КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
// Проверка доступа на запись для склада отправителя
ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.СкладОтправитель
#КонецЕсли
КОНЕЦ
И НастройкиПравДоступаПользователей.ВидОбъектаДоступа = НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
И НастройкиПравДоступаПользователей.ОбластьДанных = ЗНАЧЕНИЕ(Перечисление.ОбластиДанныхОбъектовДоступа.ПустаяСсылка)
И НастройкиПравДоступаПользователей.Пользователь = ГруппыПользователей.ГруппаПользователей
И НастройкиПравДоступаПользователей.Запись = ИСТИНА
ГДЕ
НазначениеВидовОбъектовДоступа.ГруппаПользователей = ГруппыПользователей.ГруппаПользователей
И НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа В (
ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.ПустаяСсылка)
#Если &ИспользоватьОграничениеПоОрганизации #Тогда
, ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации)
#КонецЕсли
#Если &ИспользоватьОграничениеПоПодразделения #Тогда
, ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Подразделения)
#КонецЕсли
#Если &ИспользоватьОграничениеПоСклады #Тогда
, ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
#КонецЕсли
)
И НастройкиПравДоступаПользователей.ОбъектДоступа ЕСТЬ NULL))
// Если включены ограничения по складам, то добавляем проверку доступа на запись для склада получателя
// Для проверки приходится формировать доп. запрос к регистрам назначения объектов доступа
// (!!!) Из-за доп. запроса решение является не самым оптимальным, создавая доп. нагрузку на сервер СУБД
#Если &ИспользоватьОграничениеПоСклады #Тогда
И (НЕ 1 В
(ВЫБРАТЬ ПЕРВЫЕ 1
1
ИЗ
РегистрСведений.НазначениеВидовОбъектовДоступа КАК НазначениеВидовОбъектовДоступа
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПравДоступаПользователей КАК НастройкиПравДоступаПользователей
ПО
ВЫБОР
КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
// Проверка доступа на запись по складу получателю
ТОГДА НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.СкладПолучатель
КОНЕЦ
И НастройкиПравДоступаПользователей.ВидОбъектаДоступа = НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа
И НастройкиПравДоступаПользователей.ОбластьДанных = ЗНАЧЕНИЕ(Перечисление.ОбластиДанныхОбъектовДоступа.ПустаяСсылка)
И НастройкиПравДоступаПользователей.Пользователь = ГруппыПользователей.ГруппаПользователей
И НастройкиПравДоступаПользователей.Запись = ИСТИНА
ГДЕ
НазначениеВидовОбъектовДоступа.ГруппаПользователей = ГруппыПользователей.ГруппаПользователей
И НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа В (
ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.ПустаяСсылка)
, ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Склады)
)
И НастройкиПравДоступаПользователей.ОбъектДоступа ЕСТЬ NULL))
#КонецЕсли
#КонецЕсли
На этом все!
Вот и все
Не работали с УПП и не собираетесь? Никогда не говори никогда!
Решение установлено у многих компаний и успешно используется. Кто знает с чем придется столкнуться у клиента :)