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