Как выполнить запрос в привилегированном режиме через COM-соединение

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с внешними соединениями (V83.ComConnector) разработчики часто сталкиваются с проблемой ограничения прав доступа (RLS). Даже если учетная запись, под которой устанавливается соединение, обладает широкими правами, настройки RLS могут скрывать часть данных. В этой статье мы подробно разберем, как обойти эти ограничения и выполнить запрос в привилегированном режиме, используя различные подходы: от использования стандартных модулей до динамической загрузки расширений.

Почему метод УстановитьПривилегированныйРежим() не работает напрямую

Разберем ситуацию: в синтакс-помощнике указано, что метод УстановитьПривилегированныйРежим доступен во внешнем соединении. Однако попытка вызвать его непосредственно через объект соединения, например Соединение.УстановитьПривилегированныйРежим(Истина), часто не приносит желаемого результата. Проанализируем причину.

Привилегированный режим во внешнем соединении работает только тогда, когда код выполняется во внутреннем контексте серверной базы. Если мы просто вызываем метод из внешней среды (например, из Excel или другой базы 1С), система проверяет наличие административных прав у пользователя COM-сессии. Если у пользователя нет права «Привилегированный режим», вызов будет проигнорирован или вызовет ошибку. Кроме того, действие режима распространяется только на текущий поток выполнения кода.

Способ 1. Использование существующих привилегированных модулей

Проанализируем самый простой путь. Если в целевой конфигурации уже есть общие модули с установленными свойствами «Привилегированный» и «Внешнее соединение», мы можем использовать их как «мост». Рассмотрим алгоритм действий:

  1. Найдем в конфигурации общий модуль, у которого в свойствах стоит галочка «Привилегированный».
  2. Проверим, чтобы у модуля была включена доступность для внешнего соединения.
  3. Если в модуле есть экспортная функция, принимающая текст запроса и возвращающая результат, мы можем вызвать её через COM.

Посмотрим на пример вызова такого модуля через COM-соединение:


// На стороне вызывающей базы (клиента)
Connector = Новый COMОбъект("V83.COMConnector");
Connection = Connector.Connect("Srvr='ServerName';Ref='BaseName';Usr='Login';Pwd='Password';");

// Предположим, в базе есть модуль "ОбщегоНазначенияСервер", который является привилегированным
// и содержит функцию "ВыполнитьЗапросКонфиденциально"
РезультатСтрокой = Connection.ОбщегоНазначенияСервер.ВыполнитьЗапросКонфиденциально(ТекстЗапроса);

Важно помнить, что передавать сложные типы данных (например, РезультатЗапроса или ТаблицаЗначений) напрямую через COM не рекомендуется из-за проблем с сериализацией. Лучше всего преобразовывать результат в строку JSON или массив структур внутри привилегированного модуля.

Способ 2. Динамическое подключение расширений конфигурации

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

Выясним пошагово, как это реализовать:

  1. Создадим локально файл расширения (.cfe), в котором будет один общий модуль.
  2. У модуля установим свойство «Привилегированный» и создадим в нем экспортную функцию для выполнения произвольных запросов.
  3. В коде COM-соединения передадим бинарные данные этого файла и установим расширение в целевую базу.
  4. Выполним запрос и удалим расширение, чтобы не оставлять следов в системе.

Посмотрим на пример программной загрузки расширения через COM:


// Читаем файл расширения в двоичные данные
ПутьКФайлу = "C:\Extensions\PrivilegedModeHelper.cfe";
ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);

// Передаем данные в COM-соединение (нужно предварительно сериализовать в Base64 или передать через временное хранилище)
// В целевой базе через COM вызываем менеджер расширений
Менеджер = Connection.РасширенияКонфигурации;
НовоеРасширение = Менеджер.Создать();
НовоеРасширение.Имя = "ВременноеРасширение";
НовоеРасширение.Синоним = "ВременноеРасширение для запроса";
// Записываем данные в объект расширения
// (Обратите внимание: работа с потоками через COM требует аккуратности)
НовоеРасширение.ЗаписатьДанные(Connection.Base64Value(Base64Строка(ДвоичныеДанные)));
НовоеРасширение.Записать();

Внимание: Для выполнения этой операции у пользователя COM-соединения должны быть права администратора (на управление расширениями).

Способ 3. Выполнение кода через «УстановитьПривилегированныйРежим» внутри COM

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

Нам необходимо передать контекст выполнения внутрь сервера. Пример кода для COM-объекта:


// Выполняем фрагмент кода внутри контекста COM-соединения
КодДляВыполнения = 
"УстановитьПривилегированныйРежим(Истина);
|Запрос = Новый Запрос;
|Запрос.Текст = ""ВЫБРАТЬ ПЕРВЫЕ 10 * ИЗ Справочник.Номенклатура"";
|РезультатЗапроса = Запрос.Выполнить().Выгрузить();
|// Сериализуем результат в строку, чтобы вернуть ее наружу
|ЗаписьJSON = Новый ЗаписьJSON;
|ЗаписьJSON.УстановитьСтроку();
|ЗаписатьJSON(ЗаписьJSON, РезультатЗапроса);
|ВозвратЗначение = ЗаписьJSON.Закрыть();";

// Выполняем через Connection
Результат = Connection.Eval(КодДляВыполнения); 

Использование метода Eval (или Выполнить) позволяет запустить код в контексте внешнего соединения. Если пользователь обладает правом на использование привилегированного режима, этот код отработает без проверки RLS.

Нюансы передачи результатов

Разберем критически важный момент: возвращение данных. Объект ТаблицаЗначений или РезультатЗапроса, полученный в привилегированном режиме внутри COM, является объектом 1С. При попытке передать его в Excel или другую среду, вы получите COM-объект, с которым сложно работать.

Рекомендуемый подход:

Это гарантирует, что привилегированный режим, который «закрывается» сразу после завершения выполнения блока кода, не приведет к ошибкам доступа при попытке прочитать данные из полученной таблицы значений позже.

Резюме по решению

Проанализировав все способы, можно сделать следующие выводы. Если задача разовая (как отчет в Excel), и у вас есть права администратора, используйте прямое выполнение кода через Connection.Execute (или аналоги) с предварительным вызовом УстановитьПривилегированныйРежим(Истина) внутри исполняемого блока. Если же требуется регулярная работа с данными, наиболее надежным и «чистым» способом будет создание небольшого привилегированного API в виде расширения или общего модуля в конфигурации.

← На главную