При работе с системой 1С:Предприятие 8.3 перед нами часто встает задача анализа прав доступа пользователей. Стандартные средства платформы предоставляют удобный интерфейс для назначения ролей, однако программное получение полного списка всех ролей пользователя, включая так называемые «неинтерактивные» роли, и последующая детальная проверка прав на конкретные объекты метаданных может оказаться нетривиальной задачей. В этой статье мы подробно разберем, как решить эту проблему, предоставив все необходимые инструменты и примеры кода.
Обычно, когда мы используем функции вроде ПолучитьДоступныеРоли(), мы получаем только те роли, которые явно назначены пользователю и доступны для интерактивной работы. Однако в сложных конфигурациях, особенно при использовании различных подсистем и расширений, пользователь может обладать правами через составные роли или роли, которые не предназначены для прямого выбора в пользовательском интерфейсе. Для создания отчетов по правам доступа, аудита или сложной логики безопасности, нам необходимо иметь полный контроль над этой информацией.
Давайте вместе выясним, как получить этот полный список ролей и как затем проверить права на различные объекты, такие как справочники, документы, регистры или даже общие модули.
Первым шагом к анализу прав является получение исчерпывающего списка всех ролей, которые назначены конкретному пользователю. Как мы уже отметили, стандартные функции могут не дать нам всей информации. Для этой цели нам потребуется обратиться к объекту пользователя напрямую и использовать его коллекцию ролей.
Мы используем глобальный объект Пользователи, который предоставляет нам методы для работы с пользователями системы. В частности, функция Пользователи.ПолучитьОбъектПользователя() позволит нам получить программный объект, представляющий пользователя, по его ссылке или уникальному идентификатору (GUID).
После того как мы получили объект пользователя, мы можем обратиться к его свойству Роли. Это свойство представляет собой коллекцию ссылок на роли, которые назначены данному пользователю. Используя метод Получить() этой коллекции, мы извлекаем массив ссылок на все без исключения роли пользователя.
Рассмотрим пример кода, который демонстрирует этот процесс:
// Функция для получения всех ролей пользователя (включая неинтерактивные)
//
// Параметры:
// СсылкаНаПользователя - Ссылка на объект Пользователь.
//
// Возвращает: Массив - Массив ссылок на объекты Роль.
//
Функция ПолучитьВсеРолиПользователя(СсылкаНаПользователя) Экспорт
МассивРолей = Новый Массив();
// Получаем объект пользователя по его ссылке
ПользовательОбъект = Пользователи.ПолучитьОбъектПользователя(СсылкаНаПользователя);
Если ПользовательОбъект <> Неопределено Тогда
// Получаем массив ссылок на все роли, назначенные пользователю
МассивСсылокНаРоли = ПользовательОбъект.Роли.Получить();
// Добавляем каждую ссылку в наш результирующий массив
Для Каждого СсылкаНаРоль Из МассивСсылокНаРоли Цикл
МассивРолей.Добавить(СсылкаНаРоль);
КонецЦикла;
КонецЕсли;
Возврат МассивРолей;
КонецФункции
// Пример использования:
// Предположим, у нас есть ссылка на пользователя, для которого хотим получить роли.
// В реальной системе это может быть текущий пользователь, или пользователь, выбранный из списка.
// Давайте найдем пользователя по наименованию для примера.
ПользовательДляАнализа = Пользователи.НайтиПоНаименованию("Иванов И.И.");
Если ПользовательДляАнализа = Неопределено Тогда
Сообщить("Пользователь не найден! Укажите существующего пользователя.");
Возврат;
КонецЕсли;
Сообщить("--- Список всех ролей для пользователя: " + ПользовательДляАнализа.Наименование + " ---");
ВсеРолиПользователя = ПолучитьВсеРолиПользователя(ПользовательДляАнализа.Ссылка);
Если ВсеРолиПользователя.Количество() = 0 Тогда
Сообщить("У пользователя нет назначенных ролей.");
Иначе
Сообщить("Найдено ролей: " + ВсеРолиПользователя.Количество());
Для Каждого РольСсылка Из ВсеРолиПользователя Цикл
Сообщить("- " + РольСсылка.Наименование);
КонецЦикла;
КонецЕсли;
В этом примере мы создали удобную функцию ПолучитьВсеРолиПользователя(), которая принимает ссылку на пользователя и возвращает массив ссылок на все его роли. Это будет нашей основой для дальнейшего анализа прав.
После того как у нас есть список всех ролей, следующим шагом является проверка, какие именно права на конкретные объекты метаданных имеет каждая из этих ролей. Для этого в платформе 1С:Предприятие существует специальный служебный объект ПраваДоступа и его метод ПолучитьПраваНаОбъектПоРоли().
Функция ПраваДоступа.ПолучитьПраваНаОбъектПоРоли() принимает два основных параметра:
РольСсылка: Ссылка на объект метаданных "Роль", для которой мы хотим проверить права.ОбъектМетаданных: Объект метаданных, представляющий тот элемент конфигурации, права на который нас интересуют (например, Метаданные.Справочники.Контрагенты, Метаданные.Документы.РеализацияТоваровУслуг и т.д.).Эта функция возвращает коллекцию, содержащую все права, которые данная роль имеет на указанный объект метаданных. Мы можем использовать метод Содержит() этой коллекции, чтобы проверить наличие конкретного права (например, Метаданные.Права.Чтение, Метаданные.Права.Изменение, Метаданные.Права.Добавление и т.п.).
Важно отметить, что функция ПраваДоступа.ПолучитьПраваНаОбъектПоРоли() может быть вызвана только на сервере. Это означает, что если ваш код выполняется на клиенте (например, в управляемой форме), вам потребуется инкапсулировать вызов этой функции в серверную процедуру или функцию.
Давайте разработаем вспомогательную функцию для проверки прав:
// Функция для проверки наличия определенного права на объект метаданных для конкретной роли
//
// Параметры:
// РольСсылка - Ссылка на объект Роль.
// ОбъектМетаданных - Объект метаданных (например, Метаданные.Справочники.Контрагенты).
// Право - Объект метаданных, представляющий право (например, Метаданные.Права.Чтение).
//
// Возвращает: Булево - Истина, если роль имеет указанное право, иначе Ложь.
//
Функция ИмеетРольПравоНаОбъект(РольСсылка, ОбъектМетаданных, Право) Экспорт
Попытка
// Получаем коллекцию прав, которые данная роль имеет на указанный объект метаданных
ПраваНаОбъект = ПраваДоступа.ПолучитьПраваНаОбъектПоРоли(РольСсылка, ОбъектМетаданных);
// Проверяем, содержится ли искомое право в полученной коллекции
Возврат ПраваНаОбъект.Содержит(Право);
Исключение
// В некоторых случаях (например, для объектов, у которых нет явных прав доступа,
// или при некорректном ОбъектеМетаданных) функция может вызвать исключение.
// Мы предполагаем, что если возникло исключение, то права нет.
Возврат Ложь;
КонецПопытки;
КонецФункции
// Пример использования функции ИмеетРольПравоНаОбъект:
// (Этот код должен выполняться на сервере или в серверной процедуре)
// 1. Получим ссылку на интересующую нас роль (например, одну из ролей пользователя)
// Для примера найдем роль по наименованию.
СсылкаНаРольДляПроверки = Роли.НайтиПоНаименованию("Бухгалтер");
Если СсылкаНаРольДляПроверки = Неопределено Тогда
Сообщить("Роль 'Бухгалтер' не найдена. Укажите существующую роль.");
Возврат;
КонецЕсли;
// 2. Определим объект метаданных, права на который хотим проверить
ОбъектДляПроверки = Метаданные.Справочники.Контрагенты;
// 3. Определим конкретное право, которое нас интересует
ПравоДляПроверки = Метаданные.Права.Чтение;
// 4. Выполним проверку
Если ИмеетРольПравоНаОбъект(СсылкаНаРольДляПроверки, ОбъектДляПроверки, ПравоДляПроверки) Тогда
Сообщить("Роль '" + СсылкаНаРольДляПроверки.Наименование + "' имеет право '" + Строка(ПравоДляПроверки) + "' на справочник '" + ОбъектДляПроверки.Имя + "'");
Иначе
Сообщить("Роль '" + СсылкаНаРольДляПроверки.Наименование + "' НЕ имеет право '" + Строка(ПравоДляПроверки) + "' на справочник '" + ОбъектДляПроверки.Имя + "'");
КонецЕсли;
// Проверим другое право для той же роли
ПравоДляПроверки = Метаданные.Права.Изменение;
Если ИмеетРольПравоНаОбъект(СсылкаНаРольДляПроверки, ОбъектДляПроверки, ПравоДляПроверки) Тогда
Сообщить("Роль '" + СсылкаНаРольДляПроверки.Наименование + "' имеет право '" + Строка(ПравоДляПроверки) + "' на справочник '" + ОбъектДляПроверки.Имя + "'");
Иначе
Сообщить("Роль '" + СсылкаНаРольДляПроверки.Наименование + "' НЕ имеет право '" + Строка(ПравоДляПроверки) + "' на справочник '" + ОбъектДляПроверки.Имя + "'");
КонецЕсли;
Мы видим, что функция ИмеетРольПравоНаОбъект() является достаточно универсальной и позволяет проверять любое право на любой объект метаданных, для которого платформа определяет права. Мы также добавили обработку исключений, чтобы наш код был более устойчивым к возможным ошибкам, например, если мы попытаемся проверить права на объект, который не имеет явных настроек прав доступа.
Теперь, когда у нас есть две основные функции – одна для получения всех ролей пользователя, а другая для проверки прав для конкретной роли и объекта, мы можем объединить их для создания более сложного и полезного инструмента, например, отчета по правам доступа пользователя. Такой отчет может быть незаменим для администраторов, аудиторов и разработчиков.
Рассмотрим комплексный пример, который демонстрирует, как мы можем получить все роли пользователя, а затем для каждой роли проверить права на несколько интересующих нас объектов метаданных и вывести эту информацию:
// --- КОМПЛЕКСНЫЙ ПРИМЕР: ОТЧЕТ ПО ПРАВАМ ДОСТУПА ПОЛЬЗОВАТЕЛЯ ---
// Эти две функции должны быть определены в модуле, доступном на сервере.
// Например, в общем модуле с установленным флагом "Сервер".
// Функция для получения всех ролей пользователя (включая неинтерактивные)
// (повторяем для полноты примера)
Функция ПолучитьВсеРолиПользователя(СсылкаНаПользователя) Экспорт
МассивРолей = Новый Массив();
ПользовательОбъект = Пользователи.ПолучитьОбъектПользователя(СсылкаНаПользователя);
Если ПользовательОбъект <> Неопределено Тогда
МассивСсылокНаРоли = ПользовательОбъект.Роли.Получить();
Для Каждого СсылкаНаРоль Из МассивСсылокНаРоли Цикл
МассивРолей.Добавить(СсылкаНаРоль);
КонецЦикла;
КонецЕсли;
Возврат МассивРолей;
КонецФункции
// Функция для проверки наличия определенного права на объект метаданных для конкретной роли
// (повторяем для полноты примера)
Функция ИмеетРольПравоНаОбъект(РольСсылка, ОбъектМетаданных, Право) Экспорт
Попытка
ПраваНаОбъект = ПраваДоступа.ПолучитьПраваНаОбъектПоРоли(РольСсылка, ОбъектМетаданных);
Возврат ПраваНаОбъект.Содержит(Право);
Исключение
// Если объект метаданных не имеет прав доступа, или возникла другая ошибка.
// Например, для общих модулей может не быть явных прав, они определяются ролями
// на использование модуля или вызов его экспортных функций.
// Для объектов без явных прав доступа функция может вызвать исключение.
Возврат Ложь;
КонецПопытки;
КонецФункции
// Основная процедура, которая формирует отчет
Процедура СформироватьОтчетПоПравамДоступа(СсылкаНаПользователяДляАнализа) Экспорт
Если СсылкаНаПользователяДляАнализа = Неопределено Тогда
Сообщить("Не указан пользователь для анализа!");
Возврат;
КонецЕсли;
ПользовательОбъект = СсылкаНаПользователяДляАнализа.ПолучитьОбъект();
Если ПользовательОбъект = Неопределено Тогда
Сообщить("Пользователь не найден по указанной ссылке!");
Возврат;
КонецЕсли;
Сообщить("--- Анализ прав для пользователя: " + ПользовательОбъект.Наименование + " (GUID: " + ПользовательОбъект.УникальныйИдентификатор + ") ---");
Сообщить(" ");
// 1. Получаем все роли пользователя
ВсеРолиПользователя = ПолучитьВсеРолиПользователя(СсылкаНаПользователяДляАнализа);
Если ВсеРолиПользователя.Количество() = 0 Тогда
Сообщить("У пользователя нет назначенных ролей. Дальнейший анализ прав невозможен.");
Возврат;
КонецЕсли;
Сообщить("Найдено ролей: " + ВсеРолиПользователя.Количество());
Для Каждого РольСсылка Из ВсеРолиПользователя Цикл
Сообщить(" - " + РольСсылка.Наименование);
КонецЦикла;
Сообщить(" ");
// 2. Определим список объектов метаданных и прав, которые мы хотим проверить
// Мы можем создать массив структур, где каждая структура содержит ОбъектМетаданных и МассивПравДляПроверки.
СписокПроверяемыхОбъектовИПрав = Новый Массив();
// Пример 1: Справочник "Контрагенты"
СтруктураПроверки = Новый Структура();
СтруктураПроверки.Вставить("ОбъектМетаданных", Метаданные.Справочники.Контрагенты);
МассивПрав = Новый Массив();
МассивПрав.Добавить(Метаданные.Права.Чтение);
МассивПрав.Добавить(Метаданные.Права.Изменение);
МассивПрав.Добавить(Метаданные.Права.Добавление);
СтруктураПроверки.Вставить("МассивПравДляПроверки", МассивПрав);
СписокПроверяемыхОбъектовИПрав.Добавить(СтруктураПроверки);
// Пример 2: Документ "Реализация товаров и услуг"
СтруктураПроверки = Новый Структура();
СтруктураПроверки.Вставить("ОбъектМетаданных", Метаданные.Документы.РеализацияТоваровУслуг);
МассивПрав = Новый Массив();
МассивПрав.Добавить(Метаданные.Права.Просмотр);
МассивПрав.Добавить(Метаданные.Права.Редактирование);
МассивПрав.Добавить(Метаданные.Права.ИнтерактивноеУдаление);
СтруктураПроверки.Вставить("МассивПравДляПроверки", МассивПрав);
СписокПроверяемыхОбъектовИПрав.Добавить(СтруктураПроверки);
// Пример 3: Регистр сведений "КурсыВалют" (только чтение)
СтруктураПроверки = Новый Структура();
СтруктураПроверки.Вставить("ОбъектМетаданных", Метаданные.РегистрыСведений.КурсыВалют);
МассивПрав = Новый Массив();
МассивПрав.Добавить(Метаданные.Права.Чтение);
СтруктураПроверки.Вставить("МассивПравДляПроверки", МассивПрав);
СписокПроверяемыхОбъектовИПрав.Добавить(СтруктураПроверки);
Сообщить("--- Детальный анализ прав по ролям ---");
// 3. Проходим по каждому объекту метаданных и проверяем права для каждой роли пользователя
Для Каждого ЭлементПроверки Из СписокПроверяемыхОбъектовИПрав Цикл
ОбъектДляПроверки = ЭлементПроверки.ОбъектМетаданных;
МассивПравДляПроверки = ЭлементПроверки.МассивПравДляПроверки;
Сообщить(" ");
Сообщить(">>> Объект: " + ОбъектДляПроверки.ПолноеИмя() + " <<<");
Для Каждого РольСсылка Из ВсеРолиПользователя Цикл
Сообщить(" Роль: " + РольСсылка.Наименование);
Для Каждого ПравоИзМассива Из МассивПравДляПроверки Цикл
Если ИмеетРольПравоНаОбъект(РольСсылка, ОбъектДляПроверки, ПравоИзМассива) Тогда
Сообщить(" + Имеет право: " + Строка(ПравоИзМассива));
Иначе
Сообщить(" - НЕ имеет право: " + Строка(ПравоИзМассива));
КонецЕсли;
КонецЦикла; // Конец цикла по правам
КонецЦикла; // Конец цикла по ролям
КонецЦикла; // Конец цикла по объектам
Сообщить(" ");
Сообщить("--- Отчет завершен ---");
КонецПроцедуры
// Пример вызова основной процедуры:
// Предположим, мы хотим проанализировать текущего пользователя
// СформироватьОтчетПоПравамДоступа(Пользователи.ТекущийПользователь().Ссылка);
// Или конкретного пользователя по наименованию
ПользовательДляОтчета = Пользователи.НайтиПоНаименованию("Иванов И.И.");
Если ПользовательДляОтчета <> Неопределено Тогда
СформироватьОтчетПоПравамДоступа(ПользовательДляОтчета.Ссылка);
Иначе
Сообщить("Пользователь 'Иванов И.И.' не найден для формирования отчета.");
КонецЕсли;
В этом расширенном примере мы не только получили список ролей и проверили права, но и организовали это в удобную структуру для формирования отчета. Мы рассмотрели проверку прав на разные типы объектов метаданных, такие как справочники, документы и регистры сведений. Вы можете легко дополнить список проверяемых объектов и прав в массиве СписокПроверяемыхОбъектовИПрав в соответствии с вашими задачами.
Данный подход позволяет нам проводить глубокий анализ безопасности системы, выявлять избыточные или недостаточные права, а также быстро реагировать на изменения в требованиях к доступу.
Пользователи.ПолучитьОбъектПользователя() и ПраваДоступа.ПолучитьПраваНаОбъектПоРоли() требуют серверного контекста. При разработке управляемых форм необходимо выносить вызовы этих функций в процедуры или функции, помеченные директивой &НаСервере или &НаСервереБезКонтекста.ПраваДоступа.ПолучитьПраваНаОбъектПоРоли() – это операция, которая требует некоторого времени. Для очень объемных задач, возможно, потребуется оптимизация, например, кэширование результатов или использование фоновых заданий.ИмеетРольПравоНаОбъект() может возвращать Ложь или вызывать исключение. Ваша обработка исключений должна это учитывать.ПраваДоступа.ПолучитьПраваНаОбъектПоРоли() учитывает эти динамические права при расчете.Метаданные.Права.Чтение, Метаданные.Права.Изменение и так далее. Это обеспечивает читаемость и надежность кода.Применяя эти знания и примеры кода, вы сможете эффективно управлять и анализировать систему безопасности в 1С:Предприятие 8.3, создавая надежные и информативные инструменты для контроля доступа пользователей.