Самостоятельное формирование разрешений и выдача экземпляров ключей
Чтобы построить универсальную систему прав доступа, вы можете самостоятельно формировать разрешения доступа, создавать экземпляры ключей и выдавать их пользователям. В этом случае можно контролировать права как на уровне элементов проекта, так и на уровне отдельных экземпляров сущностей (RLS).
В приложении этот вариант может совмещаться с простым вариантом, использующим автоматическую выдачу прав (подробнее).
Технология «1С:Шина» дает возможность гибко настраивать права доступа пользователей путем написания произвольного кода в обработчиках вычисления разрешений доступа и проверки наличия у пользователей ключей доступа.
Создание ключей доступа
Вы можете добавлять в проект собственные ключи доступа. Для этого предназначены элементы проекта вида КлючДоступа. Элементу проекта этого вида можно добавить параметры. Для каждого сочетания значений этих параметров «1С:Шина» создает свой уникальный экземпляр ключа.
«1С:Шина» позволяет создавать два вида ключей доступа:
- вычисляемые ключи доступа — ключи, наследуемые от типа
ВычисляемыйКлючДоступа.Объект. Для таких ключей необходимо определить обработчикПроверитьНаличиеКлючейДоступадля проверки наличия у пользователей ключей доступа соответствующего типа. Также для них доступны методы пересчетов и вызываются системные пересчеты (подробнее); - выдаваемые ключи доступа — ключи, наследуемые от типа
ВыдаваемыйКлючДоступа.Объект. Для таких ключей отсутствует обработчикПроверитьНаличиеКлючейДоступаи не выполняются никакие методы пересчетов. Вы сами определяете, какие ключи доступа должны быть у пользователей и в каких случаях их необходимо отозвать.
При создании ключей доступа следует учитывать, что от количества экземпляров вычисляемых ключей, передаваемых в обработчики проверки наличия ключей доступа, и от оптимальности алгоритмов этих обработчиков зависит производительность системы прав доступа и, как следствие, длительность операций подключения пользователей и записи объектов. Если логика системы прав прикладного решения поддерживает использование как вычисляемых, так и выдаваемых ключей доступа, рекомендуется отдавать предпочтение выдаваемым ключам доступа (подробнее).
Построение собственной системы прав
В собственной системе прав можно контролировать права как на уровне элементов проекта, так и на уровне отдельных экземпляров сущностей (RLS). Например, если чтение плана обмена разрешено, то дальше проверяется право на чтение конкретного элемента плана обмена. Если оно есть — значит, можно читать этот элемент. Если чтение запрещено — значит, этот элемент плана обмена читать нельзя. Если же чтение всего плана обмена запрещено, то права на его элементы не проверяются и чтение любого элемента запрещается.
Для реализации такой системы прав вам потребуется вручную создавать разрешения доступа и экземпляры ключей, а также устанавливать соответствие пользователей и экземпляров ключей.
Основные принципы построения системы ключей доступа
При построении системы ключей доступа нужно стремиться к сокращению типов ключей и их экземпляров:
- В проекте не должно быть ключей, которые фактически не используются.
- Не следует создавать новый тип ключа доступа, если логика выдачи прав приложения позволяет переиспользовать существующий.
- Не нужно создавать ключи доступа, правила выдачи которых дублируют правила стандартных ключей
КлючДоступаДляАутентифицированныхиКлючДоступаДляВсех.
Управление доступом в проекте
Каждый вид элементов проекта, для которого поддерживается управление доступом, имеет фиксированный набор прав, предоставляемый «1С:Шиной». Например, План обмена имеет права Создание, Чтение, Изменение, Удаление.
Каждый элемент проекта этих видов содержит свойство КонтрольДоступа. В этом свойстве вы можете перечислить, каким образом будет контролироваться доступ к тому или иному праву.

Чтобы получить возможность вручную создавать разрешения для тех прав, которые вы хотите контролировать самостоятельно, выберите способ контроля доступа РазрешенияВычисляются или РазрешенияВычисляютсяДляКаждогоОбъекта, например:

Описание свойства КонтрольДоступа в YAML-файле элемента проекта:
КонтрольДоступа:
Разрешения:
Создание: РазрешеноАутентифицированным
Чтение: РазрешенияВычисляются
ПоУмолчанию: РазрешеноАдминистраторам
Эти два способа контроля доступа отличаются следующим:
- РазрешенияВычисляются позволит вам контролировать права только на уровне всего элемента проекта;
- РазрешенияВычисляютсяДляКаждогоОбъекта позволит вам контролировать права и на уровне всего элемента проекта, и на уровне отдельных его элементов (RLS).
При выборе разрешения РазрешенияВычисляютсяДляКаждогоОбъекта становится активной и обязательной для заполнения настройка РасчетРазрешенийПо, в которой перечисляются названия полей объекта, по которым происходит расчет разрешений доступа:

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