Перейти к основному содержимому

Самостоятельное формирование разрешений и выдача экземпляров ключей

Чтобы построить универсальную систему прав доступа, вы можете самостоятельно формировать разрешения доступа, создавать экземпляры ключей и выдавать их пользователям. В этом случае можно контролировать права как на уровне элементов проекта, так и на уровне отдельных экземпляров сущностей (RLS).

примечание

В приложении этот вариант может совмещаться с простым вариантом, использующим автоматическую выдачу прав (подробнее).

Технология «1С:Исполнитель» дает возможность гибко настраивать права доступа пользователей путем написания произвольного кода в обработчиках вычисления разрешений доступа и проверки наличия у пользователей ключей доступа.

Ключи доступа, добавленные разработчиком

Вы можете добавлять в проект собственные ключи доступа. Для этого предназначены элементы проекта вида КлючДоступа. Элементу проекта этого вида можно добавить параметры. Для каждого сочетания значений этих параметров «1С:Исполнитель» создает свой уникальный экземпляр ключа.

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

Подробнее о ключах доступа

Построение собственной системы прав

В собственной системе прав можно контролировать права как на уровне элементов проекта, так и на уровне отдельных экземпляров сущностей (RLS). Например, если чтение справочника разрешено, то дальше проверяется право на чтение конкретного элемента справочника. Если оно есть — значит, можно читать этот элемент. Если чтение запрещено — значит, этот элемент справочника читать нельзя. Если же чтение всего справочника запрещено, то права на его элементы не проверяются и чтение любого элемента запрещается.

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

Основные принципы построения системы ключей доступа

При построении системы ключей доступа нужно стремиться к сокращению типов ключей и их экземпляров:

  • В проекте не должно быть ключей, которые фактически не используются.
  • Не следует создавать новый тип ключа доступа, если логика выдачи прав приложения позволяет переиспользовать существующий.
  • Не нужно создавать ключи доступа, правила выдачи которых дублируют правила стандартных ключей КлючДоступаДляАдминистратора, КлючДоступаДляАутентифицированных, КлючДоступаДляВсех и КлючДоступаПользователя.

Управление доступом в проекте

Каждый вид элементов проекта, для которого поддерживается управление доступом, имеет фиксированный набор прав, предоставляемый «1С:Исполнителем». Например, Справочник имеет права Создание, Чтение, Изменение, Удаление.

Каждый элемент проекта этих видов содержит свойство КонтрольДоступа. В этом свойстве вы можете перечислить, каким образом будет контролироваться доступ к тому или иному праву.

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

Описание свойства КонтрольДоступа в YAML-файле элемента проекта:

КонтрольДоступа:
Разрешения:
Создание: РазрешеноАутентифицированным
Чтение: РазрешенияВычисляются
ПоУмолчанию: РазрешеноАдминистраторам

Эти два способа контроля доступа отличаются следующим:

  • РазрешенияВычисляются позволит вам контролировать права только на уровне всего элемента проекта;
  • РазрешенияВычисляютсяДляКаждогоОбъекта позволит вам контролировать права и на уровне всего элемента проекта, и на уровне отдельных его элементов (RLS).

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

Пример заполнения настройки РасчетРазрешенийПо при расчете разрешений по полям Вид, Ответственный и Проект:

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

В качестве данных для расчета разрешений доступа нельзя указывать поля следующих типов:

  • реквизиты-коллекции,
  • строки неограниченной длины,
  • числа, в которых более 20 знаков в целой части и более 10 в дробной.

Эти ограничения накладываются и на все пользовательские структуры, входящие в состав полей для расчета разрешений доступа.

Если для элемента проекта выбрано разрешение РазрешенияВычисляютсяДляКаждогоОбъекта, то порождается новый тип имя-элемента-проекта.ДанныеРасчетаРазрешений. Объект данного типа содержит все поля, перечисленные в настройке РасчетРазрешенийПо элемента проекта.

В коде объекты данного типа можно создать двумя способами:

  • в качестве параметра указать сам объект, для которого должен быть порожден объект типа ДанныеРасчетаРазрешений:

    метод КонструкторИзОбъекта()
    // Создаем новый объект справочника Задачи
    // и заполняем его реквизиты
    пер НоваяЗадача = новый Задачи.Объект()
    НоваяЗадача.Наименование = "Наименование задачи" // Не используется для расчета разрешений
    НоваяЗадача.Вид = "Сложная задача" // Используется для расчета разрешений
    НоваяЗадача.Ответственный = "Иванов Дмитрий" // Используется для расчета разрешений
    НоваяЗадача.НомерПроекта = 15 // Используется для расчета разрешений

    // Создаем экземпляр типа ДанныеРасчетаРазрешений,
    // в качестве параметра конструктора передаем объект НоваяЗадача
    пер ДанныеРасчетаРазрешений = новый Задачи.ДанныеРасчетаРазрешений(НоваяЗадача)

    // Экземпляр типа ДанныеРасчетаРазрешений содержит поля,
    // которые использовались для расчета разрешений
    пер РеквизитВидЗадачи = ДанныеРасчетаРазрешений.Вид // "Сложная задача"
    пер РеквизитОтветственный = ДанныеРасчетаРазрешений.Ответственный // "Иванов Дмитрий"
    пер РеквизитНомерПроекта = ДанныеРасчетаРазрешений.НомерПроекта // 15
    ;
  • в качестве параметров явно указать все значения полей для расчета разрешений доступа:

    метод КонструкторИзАтрибутов()
    // Создаем экземпляр типа ДанныеРасчетаРазрешений,
    // в качестве аргументов передаем поля,
    // указанные в настройке РасчетРазрешенийПо
    пер ДанныеРасчетаРазрешений = новый Задачи.ДанныеРасчетаРазрешений(
    Вид = "Сложная задача",
    Ответственный = "Иванов Дмитрий",
    НомерПроекта = 15
    )

    // Экземпляр типа ДанныеРасчетаРазрешений содержит поля,
    // которые использовались для расчета разрешений
    // и передавались как аргументы конструктора
    пер РеквизитВидЗадачи = ДанныеРасчетаРазрешений.Вид // "Сложная задача"
    пер РеквизитОтветственный = ДанныеРасчетаРазрешений.Ответственный // "Иванов Дмитрий"
    пер РеквизитНомерПроекта = ДанныеРасчетаРазрешений.НомерПроекта // 15
    ;

Вычисление разрешений доступа

Предположим, вы добавили в приложение собственный ключ доступа с именем КлючиГруппСотрудников. Этот ключ имеет один параметр Группа, позволяющий указать элемент перечисления ГруппыСотрудников. Это перечисление имеет два элемента: Менеджеры и Руководители. Таким образом, вы сможете иметь два экземпляра ключа доступа, с помощью которых вы разделите всех пользователей на две группы.

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

@Обработчик
метод ВычислитьРазрешенияДоступа(): Массив<РазрешениеДоступа>

пер Разрешения: Массив<РазрешениеДоступа>

// Получим экземпляры ключей доступа
пер КлючМенеджеров = новый КлючиГруппСотрудников.Объект(ГруппыСотрудников.Менеджеры)
пер КлючРуководителей = новый КлючиГруппСотрудников.Объект(ГруппыСотрудников.Руководители)

// Создадим разрешение доступа
Разрешения.Добавить(новый РазрешениеДоступа([КлючМенеджеров, КлючРуководителей],
[Сущность.Право.Чтение]))

возврат Разрешения
;

Здесь чтение справочника в целом разрешается всем пользователям: и тем, кто имеет экземпляр КлючРуководителей, и тем, кто имеет экземпляр КлючМенеджеров.

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

@Обработчик
метод ВычислитьРазрешенияДоступаДляОбъектов(Элементы: ЧитаемыйМассив<Поставщики.ДанныеРасчетаРазрешений>):
ЧитаемоеСоответствие<Поставщики.ДанныеРасчетаРазрешений,
Массив<РазрешениеДоступа>>

пер Результат = <Поставщики.ДанныеРасчетаРазрешений, Массив<РазрешениеДоступа>>{:}

// Получим экземпляры ключей доступа
пер КлючМенеджеров = новый КлючиГруппСотрудников.Объект(ГруппыСотрудников.Менеджеры)
пер КлючРуководителей = новый КлючиГруппСотрудников.Объект(ГруппыСотрудников.Руководители)

для Элемент из Элементы
пер Разрешения = <РазрешениеДоступа>[]
// Проанализируем свойства элемента справочника, в зависимости от чего
// либо разрешим менеджерам его чтение, либо нет
если Элемент.ВажноеСвойство
Разрешения.Добавить(новый РазрешениеДоступа([КлючРуководителей],
[Сущность.Право.Чтение]))
иначе
Разрешения.Добавить(новый РазрешениеДоступа([КлючМенеджеров, КлючРуководителей],
[Сущность.Право.Чтение]))
;
Результат.Вставить(Элемент, Разрешения)
;
возврат Результат
;

Здесь, если элемент справочника имеет ВажноеСвойство == Истина, то чтение этого элемента разрешается только тем, кто имеет экземпляр КлючРуководителей. В противном случае элемент справочника могут читать и те, кто имеет экземпляр КлючРуководителей, и те, кто имеет экземпляр КлючМенеджеров.

В зависимости от выбранного способа контроля доступа в элементе проекта, вам нужны определенные обработчики:

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

После того как вы добавили в проект новый справочник, нужно запустить приложение и записать для этого справочника разрешения доступа. Однако событие ВычислитьРазрешенияДоступа не вызывается автоматически. Для его вызова используйте метод ПересчитатьРазрешенияДоступа() (например, для справочника: имя-справочника.ПересчитатьРазрешенияДоступа()). Такой вызов можно разместить в модуле проекта.

Подробнее о пересчете разрешений и экземпляров ключей

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

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

Таким образом, после добавления справочника и записи его элементов в приложении будут нужные разрешения как для самого справочника в целом, так и для его элементов.

Выдача ключей доступа

После добавления справочника и записи его элементов в системной таблице ключей пользователя будут сформированы все необходимые записи. Это происходит потому, что данные в эту таблицу добавляются в момент первого получения экземпляров ключа, которое выполняется в обработчике события ВычислитьРазрешенияДоступа:

// ...
// Получим экземпляры ключей доступа
пер КлючМенеджеров = новый КлючиГруппСотрудников.Объект(ГруппыСотрудников.Менеджеры)
пер КлючРуководителей = новый КлючиГруппСотрудников.Объект(ГруппыСотрудников.Руководители)
// ...

В этот момент у менеджера этого ключа доступа вызывается событие ПроверитьНаличиеКлючейДоступа, в обработчике которого вы формируете соответствие экземпляра ключа пользователям. Например:

@Обработчик
метод ПроверитьНаличиеКлючейДоступа(Ключи: ЧитаемыйМассив<КлючиГруппСотрудников.Объект>,
ПользователиДляПроверки: ЧитаемыйМассив<Пользователи.Объект>):
ЧитаемоеСоответствие<КлючиГруппСотрудников.Объект,
ЧитаемыйМассив<Пользователи.Ссылка>>

знч Результат = <КлючиГруппСотрудников.Объект, Массив<Пользователи.Ссылка>>{:}

// Получаем пользователей каждой из групп сотрудников
знч ГруппыСотрудников = Ключи.Преобразовать(Ключ -> Ключ.Группа)
знч Пользователи = ПользователиДляПроверки.Преобразовать(Пользователь -> Пользователь.Ссылка)
знч ПользователиПоГруппам: Соответствие<ГруппыСотрудников, Массив<Пользователи.Ссылка>>
= ПолучитьПользователейПоГруппам(ГруппыСотрудников, Пользователи)

для ЭкземплярКлюча из Ключи
знч ПользователиГруппы = ПользователиПоГруппам.ПолучитьИлиУмолчание(ЭкземплярКлюча.Группа)
если ПользователиГруппы != Неопределено
Результат.Вставить(ЭкземплярКлюча, ПользователиГруппы)
;
;

возврат Результат
;

Событие ПроверитьНаличиеКлючейДоступа вызывается в следующих случаях:

  • при выполнении обработчика ВычислитьРазрешенияДоступаДляОбъектов, если при этом были созданы новые экземпляры ключей доступа;
  • при подключении к приложению нового пользователя;
  • при явном пересчете определенных ключей доступа пользователей при вызове метода имя-ключа-доступа.ПересчитатьКлючи(<Пользователи>);
  • при явном пересчете всех ключей доступа пользователей при вызове метода Пользователи.ПересчитатьКлючиДоступа(<Пользователи>).

Обработчик события ПроверитьНаличиеКлючейДоступа в качестве параметров принимает массивы ключей и пользователей. На размеры этих массивов влияют:

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

Выдача прав на системные действия

Помимо формирования разрешений на доступ к отдельным элементам проекта, «1С:Исполнитель» позволяет вам выдавать пользователям права на системные действия. Для этого используется базовый тип СистемноеПраво. От него унаследовано перечисление ВстроенноеПраво, содержащее встроенные права на системные действия, в частности право на выполнение запросов в консоли запросов.

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

@Обработчик
метод ВычислитьСистемныеРазрешенияДоступа(): ЧитаемыйМассив<РазрешениеДоступа>
возврат [
новый РазрешениеДоступа(
[новый КлючДоступаДляАутентифицированных.Объект()],
[ВстроенноеПраво.КонсольЗапросов])
]
;

Для вызова обработчика и пересчета всех разрешений доступа используйте метод ПересчитатьСистемныеРазрешенияДоступа() типа Пользователи.

Чтобы проверить наличие права на выполнение системных действий у пользователя, вызовите метод ЕстьПраво или ПроверитьПраво типа КонтрольДоступа, например:

// Проверка наличия у пользователей права на использование консоли запросов.
// Проверка осуществляется в текущем контексте прав доступа
метод ДоступнаКонсольЗапросов(): Булево
возврат КонтрольДоступа.ЕстьПраво(ВстроенноеПраво.КонсольЗапросов)
;