v8: Элегантная реализация прав доступа в 1С 80 Ключевые слова: права,RLS,запись,чтение,видимость,просмотр
= Введение =
У меня для вас есть хорошая новость – права доступа в 1С 80 можно настроить просто и «красиво». Причем для этого даже не обязательно нужно знать новомодный RLS (Record-Level Security).
Вы увидите, что организация системы контроля прав доступа может стать увлекательным занятием, а не рутиной.
Настройка прав доступа всегда была головной болью для программиста 1С. В 77 вообще нельзя было отловить все события записи в базу данных. В 1С 80 появился RLS на изменение и событие ПередЗаписью, которые позволяют отслеживать (а следовательно и контролировать) любую запись в базу данных.
Пока что это только введение, т.к. не рассмотрены некоторые частные моменты, но уже сейчас систему можно запускать и использовать.
= RLS и контроль записи =
RLS на изменение не имеет никаких значимых преимуществ перед контролем в событии «ПередЗаписью» (в дальнейшем будем называть контроль записи), поэтому мы не будем его использовать, вот сравнительный анализ:
• RLS на 1% быстрее, чем контроль в модуле, но разве 1% - это главный критерий?
• RLS пока можно изменять только вручную, сидя в конфигураторе, контроль в модуле можно сосредоточить в одной обозримой процедуре.
• RLS использует языка запросов, который проигрывает функционалу 1С по возможностям – там нет функций, циклов, переменных и т.п.
• Написание достаточно сложного запроса RLS требует большой квалификации и тщательности от программиста
• RLS не отлаживается и не выдает диагностических сообщений.
• Т.к. все равно существуют вещи, которые нельзя сделать на RLS, а также учитывая его многочисленные недостатки, лучше сразу использовать контроль в модуле
= Контроля записи и контроль чтения =
Единственный RLS, который нельзя заменить программным кодом – это RLS на чтение. Он выполняется не для одной записи, как RLS на модификацию, а для всех объектов таблицы базы данных.
Если RLS на изменение решает задачу, чтобы не дать пользователю изменить то, что ему недоступно, то RLS на чтение решает задачу – как бы не дать пользователю увидеть чего-нибудь лишнего.
Например, чтобы пользователь видел только документы по своей организации, или введенные только им, или не видел некоторые счета (например зарплатные).
Разработка RLS на чтение – достаточно трудоемкий процесс и чаще всего его можно отложить на потом, в первую очередь разработав контроль записи. Кстати, немалая трудоемкость разработки RLS на чтение происходит из-за того, что RLS нельзя импортировать/экспортировать в текстовый файл – все приходится делать вручную.
RLS на чтение можно превосходно сочетать с нашей системой ролей.
= Организация функций для контроля записи =
Для контроля прав доступа нашим методом в каждый вызов ПередЗаписью модуля каждого объекта мы вставим вызов глобальной функции:
глПередЗаписью(ЭтотОбъект, Отказ, РежимЗаписи, РежимПроведения)
Такую вставку можно сделать вручную, а можно и пропарсив модули объектов – выгрузить модули объектов в файлы, обработав их обработкой и загрузив обратно.
Такая организация вызовов позволяет сосредоточить весь контроль прав доступа в одной функции.
= Программная и интерактивная запись =
Раньше меня мучал вопрос, как отличить программное изменение от интерактивного. На самом деле, это неважно, контроль должен быть всегда одинаковым. Пример: пользователь выполняет групповую обработку справочников, вроде бы это программное изменение, т.к. функция Записать() вызывается не из формы интерактивно, а из кода обработки, хотя на самом деле здесь также нужно контролировать права доступа пользователя на запись.
Другое дело, если у нас есть вызовы, которые нужно выполнять без контроля прав доступа. Такие вызовы будем помещать стандартно для 1С в привелигированные модули. В начале обработчика событий «ПередЗаписью» будем определять, если это вызов из привелигированного модуля, то контроль прав доступа осуществлять не будем.
Подробная методика такого определения здесь: Книга знаний: v8: Как определить, что запись вызвана программно (из привилегированного модуля);
= Ролевая структура =
Далее, оставим всего две роли – Администратор и Пользователь. Администратор имеет все права, Пользователь имеет все права, за исключением административных функций и других прав, которые не могут контролироваться в процедуре ПриЗаписи, например открытие и запуск внешних обработок. Если пользователю это понадобится, нужно будет создать ему дополнительную роль, например «ОткрытиеВнешнихОбработок» и добавлять нужным пользователям нужные права.
Список ролей пользователей удобнее вести не в конфигураторе, а в служебном справочнике. Преимущества:
= Можно на лету создавать новые роли.
= Можно создавать роли в режиме 1С-предприятия, которое может быть открыто у нескольких администраторов, конфигуратор может быть открыт только один.
= Сердце контроля =
Сердце контроля – функция «ОбъектДоступ(П)», которая возвращает логическое значение: истина – доступ есть, ложь – доступа нет.
Функция «глПередЗаписью» выглядит так:
Функция глПередЗаписью(ЭтотОбъект, Отказ, РежимЗаписи=Неопределено, РежимПроведения=Неопределено)
Отказ=НЕ ОбъектДоступ(Новый
Структура(«Событие, Объект,Отказ, РежимЗаписи, РежимПроведения»,
«ПередЗаписью», Отказ, РежимЗаписи, РежимПроведения));
Возврат НЕ Отказ;
КонецФункции
Функция ОбъектДоступ(П)
КонецФункции
Заметим, что мы не проверяем, вдруг Отказ уже равно истине, т.к. для некоторых целей, например для протоколирования, нам все равно понадобится вызов функции ОбъектДоступ. К тому же обычно вызов нашей функции идет самым первым в процедуре «ПередЗаписью».
В дальнейшем переданную в функцию ОбъектДоступ структуру П будем называть структурой доступа, а саму функцию – функцией доступа. Функция ОбъектДоступ может рекурсивно вызывать себя, формируя из текущей структуры доступа новые структуры доступа.
= Ролевой контроль =
Если у пользователя имеется несколько ролей, то функция доступа вызывается для каждой из ролей пользователя, пока не получит доступ хотя бы по одной из роли. Т.е. происходит логическое сложение прав доступа. Если есть доступ хотя бы по одной роли, то значит есть доступ.
При каждом вызове происходит добавление в структуру доступа свойства Роль=…., где значением выступает идентификатор роли пользователя. Например Роль=«Кладовщик».
= Контроль записи в базу =
При записи в базу данных у нас есть версия объекта, хранимая в базе данных и версия объекта в памяти, которой будет замещена версия в базе. Контроль вызывается два раза – сначала для объекта в базе данных, затем, если пользователь может изменять объект, тогда уже для объекта в памяти.
Так как практически любое изменение связано с записью, то контроль практически всегда вызывается дважды.
Для новых объектов контроль объекта в базе данных отсутствует.
При контроле объекта в базе данных в структуру доступа добавляется свойство Подконтрольный=«База», в памяти - Подконтрольный=«Память».
= Открытие формы =
Для пользователя удобно информировать о том, что он не может ничего сделать с объектом. Самый наглядный способ – открытие формы в режиме ТолькоПросмотр=истина, когда все элементы управления, связанные с данными или имеющие флаг «ИзменяетДанные», недоступны.
Нужно пропарсить все формы и в событие ПриОткрытии (когда форма уже создана) вставить код:
глПриОткрытииФормыОбъекта(ЭтаФорма);
Функция глПриОткрытииФормыОбъекта выглядит так:
Функция глПередЗаписью(Форма)
Отказ=НЕ ОбъектДоступ(Новый
Структура(«Событие, Форма»,
«ПриОткрытии», Форма));
Форма.ТолькоПросмотр=Отказ;
КонецФункции
Если есть желание ограничивать пользователю просмотр данных в объекте без RLS на чтение, нужно вставлять код в событие формы «ПередОткрытием»:
глПередОткрытиемФормыОбъекта(ЭтаФорма, Отказ, СтандартнаяОбработка);
Функция глПередОткрытиемФормыОбъекта выглядит так:
Функция глПередЗаписью(Форма, Отказ, СтандартнаяОбработка)
П=Новый
Структура(«Событие, Форма»,
«ПередОткрытиемОткрытии», Форма);
Отказ=НЕ ОбъектДоступ(П);
СтандартнаяОбработка=П.СтандартнаяОбработка;
Возврат НЕ Отказ;
КонецФункции
= Проверка права =
Для целей составления отчетности по правам – например, чтобы узнать, а что может делать в базе пользователь и с какими объектами, можно вызывать функцию доступа на конкретное событие без непосредственной записи объекта, например:
Доступ=ОбъектДоступ(Новый
Структура(«Событие, Объект,Отказ, РежимЗаписи, РежимПроведения»,
«ПередЗаписью», Отказ, РежимЗаписи, РежимПроведения));
= Интерфейсы =
Раз уж мы в настройке прав руководствуемся соображениями красоты и лени, то и к интерфейсам нужно применять то же правило.
Самый простой способ сделать это – создать по одному интерфейсу на каждый пункт интерфейса. Далее, при открытии системы или при переключении интерфейса, нужно определять, какие пункты доступны данному пользователю
К сожалению, 80 не позволяет получить состав интерфейса и действия, осуществляемые при выборе пункта меню, щелчке на пиктограмме.
Поэтому для экономии времени можно кодировать имя пункта меню (а следовательно и названия интерфейса), например так:
• Документ_ПриходнаяНакладная_ОткрытьЖурнал
• Документ_ПриходнаяНакладная_Ввести
• Отчет_Шахматка_Открыть
Далее можно перебрать по метаданным все интерфейсы, проверить какие из них соответствуют доступным объектам и вывести только их.
= Протоколирование изменений =
Если вы хотите настроить собственное протоколирование изменений данных, то это как раз и удобно делать в нашей процедуре «глПередЗаписью».
Рекомендации:
• Можно протоколировать не все изменения, а только изменения конкретными пользователями или конкретных объектов.
• Можно сравнивать текущую версию объекта с его версией, хранящейся в базе данных – такое сравнение можно организовать и протоколировать все изменения, внесенные пользователем, вплоть до конкретного реквизита.
= Контроль прав и обмены данными =
Следует также учесть еще один момент, не относящийся к правам доступа – при обмене данными часто не контролируется правильность записи объектов, т.е. в коде типовых конфигураций почти в каждой процедуре «ПередЗаписью» вы можете увидеть код вроде:
Если ОбменДанным.Загрузка=истина Тогда
Возврат;
КонецЕсли;
Он означает, что при загрузке не нужно контролировать правильность данных.
Наш код по контролю прав (вызов функции глПередЗаписью) доступа нужно помещать перед такими проверками, т.к. даже в случае обмена данными должны контролироваться права доступа пользователя, а если они не должны контролироваться, то записи при обменах надо вызывать из привелигированных модулей.
= Вариант функции контроля прав доступа =
Вот пример функции контроля прав доступа из рабочей конфигурации:
Функция обДоступ(П) Экспорт
Если обДоступПравоИмя(П)<>Неопределено Тогда //Если указано право доступа
Возврат обДоступПравоЕсть(П);
КонецЕсли;
Возврат Истина; //По умолчанию доступ есть
КонецФункции
Функция обДоступЕстьРоль(П, Роль)
Возврат обДоступРолиПользователя(П).Содержит(Метаданные.Роли[Роль]);
КонецФункции
Функция обДоступРолиПользователя(П)
Если НЕ П.Свойство("РолиПользователя") Тогда
П.Вставить("РолиПользователя", ПользователиИнформационнойБазы.ТекущийПользователь().Роли);
КонецЕсли;
Возврат П.РолиПользователя;
КонецФункции
Функция обДоступПравоИмя(П)
Перем Право;
Если НЕ П.Свойство("Право", Право) Тогда
П.Вставить("Право", Право);
КонецЕсли;
Возврат П.Право;
КонецФункции
Функция обДоступПравоЕсть(П)
Право=обДоступПравоИмя(П);
Если Право="ВИДИМОСТЬКОЛОНКИДРУГОЕИМЯУСОТРУДНИКОВ" Тогда
Возврат обДоступЕстьРоль(П, "ОТДЕЛКАДРОВ");
КонецЕсли;
Возврат П.Право;
КонецФункции
Все начинается с вызова функции обДоступ со структурой параметров. Обращения к дополнительно вычисляемым параметрам - список доступных ролей, имя текущего пользователя и т.п. хешируются, т.е. добавляются в структуру и при повторном вызове не вычисляются (экономим время).
Т.е. весь контроль прав реализуется на вызове обДоступ с названием требуемого права и описания в структуре контекста вызова (ссылку на форму или объект). Т.е. в поле Право заносим строку с названием проверяемого права, а в параметр Форма или Объект заносим текущую форму или текущий объект.
Функцию обДоступПравоЕсть можно реализовать через Выполнить, т.е. брать ее текст из константы. Это поможет динамической реализации прав доступа.
Пример вызова:
ЭлементыФормы.СправочникСписок.Колонки.ДругоеИмя.Видимость=обДоступ(Новый Структура("Право, Форма", "ВИДИМОСТЬКОЛОНКИДРУГОЕИМЯУСОТРУДНИКОВ", ЭтаФорма))
= Контроль запросов =
Можно обойтись даже без РЛС на чтение, если контролировать все запросы, выполняемые пользователем.
Это здесь:
Книга знаний: v8: Контроль над исполнением всех запросов в 1С
= Полезные ссылки =
Полезно реализовать контроль прав доступа в виде регистра правил:
Книга знаний: v8: Регистр правил для контроля прав доступа |