6 ошибок настройки RLS в 1С: чужие данные видны

6 ошибок настройки RLS в 1С: чужие данные видны

Почему RLS в 1С — это не просто «галочка в настройках»

Механизм Row Level Security (RLS) — ограничение доступа на уровне записей — один из самых мощных и одновременно самых коварных инструментов платформы 1С:Предприятие 8. Когда он настроен правильно, пользователь видит ровно те данные, которые ему положены: свои заказы, свои контрагенты, свои склады. Когда настроен с ошибкой — менеджер Иванов спокойно открывает документы менеджера Петрова, а директор филиала видит выручку всей компании.

Проблема усугубляется тем, что ошибки RLS не бросаются в глаза. Система не падает с ошибкой, не пишет в журнал регистрации критических событий. Пользователь просто видит данные — и не подозревает, что видит лишнее. Администратор тоже не замечает, потому что сам работает с полными правами.

В этой статье мы разберём шесть наиболее распространённых ошибок при настройке RLS, покажем, как они выглядят в коде шаблонов ограничений, и объясним, как каждую из них исправить. Если вы хотите делегировать эту работу профессионалу — найти разработчика 1С можно на нашей бирже.

Ошибка №1: Пустой шаблон ограничения — доступ открыт всем

Как это выглядит

Самая «детская» и при этом крайне распространённая ошибка. Администратор создаёт роль, переходит на вкладку «Ограничения доступа к данным», добавляет ограничение для объекта — и оставляет поле шаблона пустым или вводит туда текст, который всегда возвращает ИСТИНА.

В конфигураторе это выглядит так: объект добавлен в список ограничений, флажок «Ограничить» установлен, но в поле условия написано просто:

ИСТИНА

Или ещё хуже — поле вообще не заполнено, и платформа интерпретирует пустое условие как «разрешить всё».

Почему это опасно

Пользователь с такой ролью получает доступ ко всем записям объекта без исключения. При этом в правах роли формально написано «ограничен» — и администратор уверен, что всё настроено. Аудит безопасности покажет наличие ограничения, но не его содержимое.

Как исправить

Всегда проверяйте текст условия после сохранения. Минимально корректное ограничение для фильтрации по менеджеру выглядит так:

// Шаблон ограничения: Документ.РеализацияТоваровУслуг.Чтение
// Условие: пользователь видит только свои документы

{ГДЕ
	(ТекущаяТаблица.Менеджер = &ТекущийПользователь)
}

А параметр &ТекущийПользователь должен быть установлен в сеансовых данных:

// Модуль сеанса — установка параметров сеанса
Процедура УстановкаПараметровСеанса(ТребуемыеПараметры)

	// Устанавливаем текущего пользователя для RLS
	ПараметрыСеанса.ТекущийПользователь = Справочники.Пользователи.НайтиПоРеквизиту(
		"ПользовательИБ",
		ПользователиИнформационнойБазы.ТекущийПользователь()
	);

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

Ошибка №2: Отсутствие ограничения на табличные части

Суть проблемы

Разработчик добавляет ограничение на чтение шапки документа, но забывает про его табличные части. В платформе 1С ограничения на основную таблицу и таблицы табличных частей настраиваются отдельно. Если ограничить только шапку, пользователь не увидит документ в списке — но сможет получить данные строк через запрос напрямую к таблице табличной части.

Пример уязвимости

Представьте: у вас настроено ограничение на Документ.ЗаказКлиента — пользователь видит только заказы своего менеджера. Но ограничение на Документ.ЗаказКлиента.Товары не установлено. Тогда следующий запрос вернёт строки чужих заказов:

// ВНИМАНИЕ: этот запрос обходит RLS на шапку документа!
// Данные табличной части доступны без ограничения

Запрос = Новый Запрос;
Запрос.Текст =
	"ВЫБРАТЬ
	|	Товары.Номенклатура,
	|	Товары.Количество,
	|	Товары.Цена,
	|	Товары.Ссылка КАК Документ
	|ИЗ
	|	Документ.ЗаказКлиента.Товары КАК Товары";

Результат = Запрос.Выполнить();
// Пользователь получит строки ВСЕХ заказов, не только своих!

Правильная настройка

Для каждого объекта с табличными частями необходимо добавлять ограничения на каждую таблицу отдельно. Условие для табличной части обычно повторяет условие шапки, но обращается к полю через точку:

// Шаблон ограничения для табличной части: Чтение
// Объект: Документ.ЗаказКлиента.Товары

{ГДЕ
	(ТекущаяТаблица.Ссылка.Менеджер = &ТекущийПользователь)
}

Обратите внимание: ТекущаяТаблица.Ссылка — это ссылка на документ-владелец табличной части. Через неё можно обращаться к любым реквизитам шапки.

Ошибка №3: Некорректная логика «ИЛИ» в условиях ограничения

Как возникает ошибка

Когда пользователь должен видеть данные по нескольким критериям одновременно (например, свои документы или документы своего подразделения), разработчики часто строят условие через ИЛИ. Ошибка возникает, когда одна из ветвей условия оказывается всегда истинной из-за некорректной проверки на NULL или пустое значение.

// НЕПРАВИЛЬНО: если ОтветственноеПодразделение не заполнено,
// условие справа от ИЛИ всегда истинно — пользователь видит всё!

{ГДЕ
	(ТекущаяТаблица.Менеджер = &ТекущийПользователь)
	ИЛИ (ТекущаяТаблица.Подразделение = &ПодразделениеПользователя)
}

Если параметр &ПодразделениеПользователя не установлен или установлен в пустую ссылку, а в документах поле «Подразделение» тоже пустое — условие ТекущаяТаблица.Подразделение = &ПодразделениеПользователя вернёт ИСТИНА для всех записей с пустым подразделением. В зависимости от данных это может открыть доступ к большей части базы.

Правильный подход

Всегда явно проверяйте, что параметр не пустой, прежде чем использовать его в условии:

// ПРАВИЛЬНО: проверяем заполненность параметра

{ГДЕ
	(ТекущаяТаблица.Менеджер = &ТекущийПользователь)
	ИЛИ (
		&ПодразделениеПользователя <> ЗНАЧЕНИЕ(Справочник.Подразделения.ПустаяСсылка)
		И ТекущаяТаблица.Подразделение = &ПодразделениеПользователя
	)
}

И в модуле сеанса добавьте защитную проверку:

Процедура УстановкаПараметровСеанса(ТребуемыеПараметры)

	Пользователь = Справочники.Пользователи.НайтиПоРеквизиту(
		"ПользовательИБ",
		ПользователиИнформационнойБазы.ТекущийПользователь()
	);

	ПараметрыСеанса.ТекущийПользователь = Пользователь;

	// Устанавливаем подразделение только если пользователь найден
	Если НЕ Пользователь.Пустая() Тогда
		ПараметрыСеанса.ПодразделениеПользователя = Пользователь.ОсновноеПодразделение;
	Иначе
		// Пустая ссылка — пользователь не увидит ничего лишнего
		ПараметрыСеанса.ПодразделениеПользователя =
			Справочники.Подразделения.ПустаяСсылка();
	КонецЕсли;

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

Ошибка №4: Забытые регистры и виртуальные таблицы

Регистры остаются без ограничений

Классический сценарий: разработчик настраивает RLS на справочники и документы, но забывает про регистры. Между тем регистры накопления, регистры сведений и регистры бухгалтерии хранят огромные массивы аналитических данных — обороты, остатки, проводки.

Пользователь, у которого закрыт доступ к документам продаж, может через регистр накопления «Продажи» получить все обороты по всем контрагентам. Это особенно актуально для конфигураций на базе 1С:Бухгалтерия на Кодерион, где регистры бухгалтерии содержат полную финансовую картину.

Виртуальные таблицы — отдельная история

Ещё тоньше проблема с виртуальными таблицами регистров (Остатки, Обороты, ОстаткиИОбороты). Ограничение, настроенное на основную таблицу регистра, не распространяется автоматически на виртуальные таблицы. Их нужно ограничивать отдельно.

// Шаблон ограничения для основной таблицы регистра:
// РегистрНакопления.Продажи.Чтение

{ГДЕ
	(ТекущаяТаблица.Менеджер = &ТекущийПользователь)
}

// Этот шаблон НЕ защищает виртуальные таблицы!
// Нужно отдельно добавить ограничение для:
// РегистрНакопления.Продажи.Остатки
// РегистрНакопления.Продажи.Обороты
// РегистрНакопления.Продажи.ОстаткиИОбороты

Для виртуальных таблиц условие аналогично, но обращение к полям идёт без префикса основной таблицы:

// Шаблон ограничения для виртуальной таблицы:
// РегистрНакопления.Продажи.Обороты.Чтение

{ГДЕ
	(ТекущаяТаблица.Менеджер = &ТекущийПользователь)
}

Чек-лист объектов для ограничения

  • Справочники (основная таблица)
  • Документы (основная таблица + все табличные части)
  • Регистры накопления (основная таблица + Остатки + Обороты + ОстаткиИОбороты)
  • Регистры сведений (основная таблица + срезы)
  • Регистры бухгалтерии (основная таблица + виртуальные таблицы)
  • Планы видов характеристик
  • Бизнес-процессы и задачи

Ошибка №5: Конфликт нескольких ролей — расширение вместо пересечения

Как работает объединение ролей в 1С

Это одна из самых концептуально сложных ошибок. Когда пользователю назначено несколько ролей, платформа объединяет их права по принципу логического ИЛИ: пользователь получает максимум из того, что разрешено хотя бы в одной роли. Для обычных прав это интуитивно понятно. Но с RLS всё иначе.

Если одна роль содержит ограничение Менеджер = &ТекущийПользователь, а вторая роль на тот же объект не содержит ограничения (или содержит условие ИСТИНА) — итоговый доступ будет без ограничений. Ограничение из первой роли полностью нивелируется второй ролью.

// Роль «МенеджерПродаж»: ограничение на документы
// Условие: ТекущаяТаблица.Менеджер = &ТекущийПользователь

// Роль «ПросмотрОтчётов»: без ограничения на те же документы
// Условие: (пусто) = ИСТИНА

// Итог для пользователя с обеими ролями:
// ИСТИНА ИЛИ (Менеджер = ТекущийПользователь) = ИСТИНА
// Пользователь видит ВСЕ документы!

Как диагностировать проблему

Используйте встроенный инструмент платформы — «Проверка прав доступа» в конфигураторе. Но лучше напишите диагностическую процедуру:

// Диагностика: получить список ролей пользователя и их ограничений
Процедура ДиагностикаРолейПользователя(ПользовательИБ)

	Сообщение = Новый СообщениеПользователю;
	Сообщение.Текст = "Роли пользователя: " + ПользовательИБ.Имя;
	Сообщение.Сообщить();

	Для Каждого Роль Из ПользовательИБ.Роли Цикл
		Сообщение = Новый СообщениеПользователю;
		Сообщение.Текст = " - " + Роль.Роль.Имя;
		Сообщение.Сообщить();
	КонецЦикла;

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

Правильная архитектура ролей

Золотое правило: если объект ограничен в одной роли, он должен быть ограничен во всех ролях, которые дают к нему доступ. Никогда не оставляйте ограничение пустым в «вспомогательных» ролях — это немедленно открывает полный доступ.

Рекомендуемый подход — создать базовую роль с ограничениями и наследовать от неё, а не создавать множество независимых ролей с частичными настройками. Подробнее об архитектуре прав можно почитать в контексте задачи по 1С:ERP, где многоуровневая система доступа является нормой.

Ошибка №6: Параметры сеанса не инициализированы — доступ закрыт или открыт полностью

Два сценария одной ошибки

Параметры сеанса — это механизм, через который RLS получает «контекст» текущего пользователя. Если параметр не установлен, возможны два сценария в зависимости от настроек платформы:

  1. Сценарий «всё закрыто»: запрос с неинициализированным параметром возвращает пустую выборку. Пользователь видит пустые списки и думает, что данных нет. Обращается в поддержку — теряется время.
  2. Сценарий «всё открыто»: если условие написано с ошибкой и NULL-параметр сравнивается с полем, которое тоже может быть NULL — условие парадоксально выполняется для всех записей.

Типичная ошибка в модуле сеанса

// НЕПРАВИЛЬНО: параметр устанавливается только при определённых условиях
// Если условие не выполнено — параметр остаётся неинициализированным

Процедура УстановкаПараметровСеанса(ТребуемыеПараметры)

	Пользователь = Справочники.Пользователи.НайтиПоРеквизиту(
		"ПользовательИБ",
		ПользователиИнформационнойБазы.ТекущийПользователь()
	);

	// Ошибка: параметр не устанавливается если пользователь не найден!
	Если НЕ Пользователь.Пустая() Тогда
		ПараметрыСеанса.ТекущийПользователь = Пользователь;
		ПараметрыСеанса.ДоступныеОрганизации = Пользователь.ДоступныеОрганизации.Выгрузить();
	КонецЕсли;
	// Если пользователь не найден — параметры не установлены!
	// При первом обращении к данным платформа выдаст ошибку
	// или вернёт некорректный результат

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

Правильная инициализация с защитой

// ПРАВИЛЬНО: параметры устанавливаются всегда, даже в «безопасные» значения

Процедура УстановкаПараметровСеанса(ТребуемыеПараметры)

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

	// Пытаемся найти пользователя
	ПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();

	Если ПользовательИБ = Неопределено Тогда
		// Системный пользователь или неавторизованный сеанс
		// Параметры уже установлены в безопасные значения
		Возврат;
	КонецЕсли;

	Пользователь = Справочники.Пользователи.НайтиПоРеквизиту(
		"ПользовательИБ",
		ПользовательИБ
	);

	Если Пользователь.Пустая() Тогда
		// Пользователь ИБ не связан со справочником Пользователи
		// Логируем проблему и оставляем безопасные значения
		ЗаписьЖурналаРегистрации(
			"RLS.ОшибкаИнициализации",
			УровеньЖурналаРегистрации.Предупреждение,
			,
			,
			"Пользователь ИБ не найден в справочнике: " + ПользовательИБ.Имя
		);
		Возврат;
	КонецЕсли;

	// Устанавливаем реальные значения
	ПараметрыСеанса.ТекущийПользователь = Пользователь;

	// Формируем список доступных организаций
	ДоступныеОрганизации = Новый СписокЗначений;
	Для Каждого СтрокаОрганизации Из Пользователь.ДоступныеОрганизации Цикл
		ДоступныеОрганизации.Добавить(СтрокаОрганизации.Организация);
	КонецЦикла;
	ПараметрыСеанса.ДоступныеОрганизации = ДоступныеОрганизации;

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

Найдите специалиста для решения этой задачи на koderion.ru