Дискретный доступ
Contents
Дискретный доступ#
Дискретный доступ позволяет выдавать привилегии в рамках объектов классов или записей в администрируемой выборке.
Настройка привилегии#
Административный объект#
Открыть карточку
Адм. объекта
Установить признак
Дискретный доступ
Перейти на закладку
Дискретные ограничения доступа
Создать
Дискретное ограничение
.
Дискретное ограничение доступа#
Создать новую запись или встать на запись для редактирования
Указать уникальное в рамка адм. объекта имя и наименование
Настроить тип правила
Настроить параметры правила.
На закладке
Скрипт для фильтрации объектных привилегий
написать sql-запрос, который будет фильтровать списки объектовНа закладке
Скрипт проверки строк по объектному кешу
написать jexl-скрипт, который будет по rop-у объекта или строке выборки возвращать признак, что условие выполнилось для этого объекта.
Тип правила#
Доступны 3 варианта типа правила:
Примитивное
Составное
Без параметров
Примитивное правило#
Состоит из одного параметра.
При анализе прав пользователя со всех его ролей будут собраны значения параметров и собраны в один массив примитивных значений.
Например для числового правила будет собран массив вида: [10, 20, 30]
Составное правило#
Состоит из нескольких параметров.
При анализе прав пользователя используется следующий алгоритм:
Для каждого набора значений, указанных на роли, формируется json-объект вида:
{ <ИД параметра 1>: [<Массив указанных значений>], <ИД параметра 2>: [<Массив указанных значений>], ... <ИД параметра n>: [<Массив указанных значений>] }
Каждая роль может обладать несколькими наборами значений на одно правило. По этому результатом агрегации настроенных значений на роли будет массив, содержащий объекты, указанные в п.1:
[ {json-объект для набора 1}, {json-объект для набора 2}, ... {json-объект для набора n}, ]
При агрегации прав пользователя:
со всех ролей анализируются массивы, описанные в п.2
в json-е заменяются ИД параметров на их имена
каждый элемент массива приводится к строке
собирается результирующий массив, содержащий уникальные строки.
Таким образом исключаются дублирующие настройки на ролях.
Пример агрегации прав пользователя:
[
{
"paramName1": ["a", "b", "c"],
"paramName2": [1, 2, 3]
},
{
"paramName1": ["c", "d", "e"],
"paramName2": [3, 4, 5]
}
]
Правило без параметров#
Не содержит параметров.
При анализе прав пользователя, если хотя на одной роли будет правило без параметров, оно будет проверяться.
Параметры правила#
Позволяют настроить тип данных и ссылочность для значений, которые будут указаны в зависимости от выбранного типа определения:
„Обычный“: значения параметров определяются на ролях.
„Динамический“: значения параметров определяются динамически по скрипту динамического определения под параметром в настройке дискретного доступа на одминистрируемом объекте. на ролях.
„Определяется профилем“: значения параметров в этом случае определяются от параметров с подчиненных профилей пользователя.
{important} Если для роли назначен параметр с типом «Определяется профилем», сохранить роль можно только если она имеет тип «Мастер-роль».
Если правило примитивное, то у него доступен для настройки только один параметр с системным именем «SimpleAcObjectRuleParam». Для составного правила доступны несколько параметров.
Динамическое определение значения параметров#
Для некоторых случаев бывает удобнее не задавать на каждую роль набор параметров, а определять их динамически, например от текущего пользователя или даты. Для этого на роли в настройке правила дискретного доступа нужно:
В карточке администрируемого объекта, на закладке с параметрами под динамическими параметрами написать jexl-скрипт, который возвращает массив строк Пример:
var idvUser = Btk_UserApi.getCurrentUserID(); //получаем id текущего пользователя
var res =[...]; //объявляем массив для сбора значений параметра
if (idvUser != null){
var rvUser = Btk_UserApi.load(idvUser);
var idvObjAttr = Btk_UserApi.getAttrValue(rvUser, "idObjAttr"); // получаем значение атрибута с именем "idObjAttr", хранящийся для текущего пользователя
if (!empty idvBisObj){
res.add(idvBisObj.toString()); // добавляем полученное значение в массив
}
}
res; // возвращаем массив со значениями
На роли, на закладке с настройками дискретного доступа для нашего правила у параметра выбираем тип «Динамический».
Скрипт для фильтрации объектных привилегий#
Используется для фильтрации списков объектов, через наложение макроса универсального фильтра &DefUniFltMacros
.
Доступные макросы внутри текста скрипта:
(&<Имя атрибута>)
Будет заменено на указанное имя атрибута. Например,t.id
Для фильтрации доступны все атрибуты дата-сета выборки шапки БО.(¶ms)
Массив строк, в котором будут параметры, собранные со всех ролей пользователя.(&CurrentUserID)
Будет заменено на ИД текущего пользователя.(&CurrentUserName)
Будет заменено на имя текущего пользователя.
Запрос чаще всего представляет из себя exist
, в котором джойнится таблица класса, и через массив значений накладываются условия фильтрации.
Скрипт проверки строк по объектному кешу#
Используется для проверки ограничения по объектам, используя объектный кеш.
Представляет из себя jexl-скрипт
, который возвращает true
, если объект проходит условие, и false
- если не проходит.
Доступные переменные:
row
\Если адм. объект создан по классу, то в этой переменной будет
rop
проверяемого объекта. Это не исходныйrop
, а его оберткаJexlRop
, которая позволяет обращаться к полям объекта.Совет
Для получения исходного
rop
воспользуйтесь следующим примером://в переменной jexlRop находится объект класса JexlRop var rop = jexlRop.data()
Если адм. объект создан не по классу, то здесь будет json-представление строки дата-сета выборки.
params
массив значений параметров, собранных со всех ролей пользователя.если правило примитивное - то будет содержать примитивы (строка или число)
если правило составное - то будет содержать строки, каждая из которых - json
Роль#
Открыть карточку роли или выбрать ее в списке;
перейти на закладку
Дискретный доступ
;создать новую запись, выбрав адм. объект;
указать значение параметров для ограничений;
сопоставить объектные и элементарные привилегии с нужным значением параметров ограничений, заполнив поле
Ограничение дискретного доступа
Ограничение доступа к объектам класса.#
Выдается через объектные привилегии с системными именами edit#
и view#
Совет
Если таких привилегий нет у адм. объекта, то выполните его синхронизацию.
Дискретные ограничения применяются только, если:
это главная выборка адм. объекта (шапка БО);
это не супер-пользователь;
к адм. объекту приминаются настройки администрирования;
адм. объект имеет признак
Дискретный доступ
.
Определение объектов для проверки дискретных прав#
При проверке дискретных привилегий строки, в контексте которых требуется проверить наличие прав, определяются методом acRows
Алгоритм работы метода:
Если выборка принадлежит классу (
thisApi() != null
), то для всех выделенных записей через поле первичного ключа загружаютсяпровайдеры строк
, из них через полеgidRoot_dz
определяются шапки БОЕсли выборка не принадлежит классу (
thisApi() == null
), то по всем выделенным строкам собирается json-массив, содержащий на каждую строку json-объект, в котором ключ - имя атрибута, значение - значение атрибута.
Ограничение списков#
Используя значения параметров всех ролей и настроенные «Скрипты для фильтрации объектных привилегий» формируется условие ограничения и накладывается фильтрация через макрос &DefUniFltMacros
.
Ограничение открытия карточек#
На открытие карточки используя значения параметров всех ролей выполняется jexl-скрипт
, настроенный в Скрипт проверки строк по объектному кешу
. Если у пользователя нет прав на объект, то будет выдана ошибка.
Ограничение на редактирование объектов#
На beforeEdit
или delete
проверяется наличие привилегии edit#
(выполняется jexl-скрипт
, настроенный в Скрипт проверки строк по объектному кешу
). Если у пользователя нет прав на объект, то будет выдана ошибка.
Ограничение на объектные привилегии#
Если для объектной привилегии указано Ограничение дискретного доступа
, то такая привилегия может быть проверена только в контексте какого-либо объекта.
Для проверки объектных привилегий используется метод Btk_AdminPkg().hasObjPriv
.
Пример:
if (Btk_AdminPkg().hasObjPriv("SomeObjectName", "SomePrivName", Seq(rop))) {
//код, выполняемый при наличии привилегии
}
Если для привилегии, имеющей ограничение, выполнить проверку без передачи объектов, то будет выдана ошибка.
Ограничение на элементарные привилегии#
Для элементарной привилегии может быть указано ограничение дискретного доступа. Если ограничение указано, то при выполнении операции будут проверены права в контексте записей в выборке.
На загрузку выборки или перезагрузку ClassLoader
-а происходит подписка на событие выполнения операции.
При выполнении операции проверяются дискретные права на эту элементарную привилегию:
Получение
acRows
Для полученных строк выполняется
jexl-скрипт
, настроенный вСкрипт проверки строк по объектному кешу
Если прав нет, то выдается ошибка.
Принудительное включение и отключение проверки дискретного доступа#
Принудительное включение или отключение проверки дискретного доступа позволяет игнорировать настройки дискретного доступа на административном объекте.
Признак устанавливается через параметр UseDiscreteAccess
при открытии lookUp-формы
через метод-обработчик.
Таким образом, установка параметра UseDiscreteAccess
в значение равное 1
позволит принудительно применить настройки дискретного доступа, независимо от настройки на административном объекте.
Пример принудительного включения проверки дискретного доступа:
Bs_BisObjAvi.processIdMCEvent(
event,
{ id => setClientidBisObjFrom(editRef(), id) },
params = Map(RefEventParam.UseDiscreteAccess -> 1)
)
При установка значения параметра UseDiscreteAccess
в значение равное 0
настройки дискретного доступа будут игнорироваться, вне зависимости от настройки дискретного доступа на административном объекте.
Пример принудительного выключения проверки дискретного доступа:
Bs_BisObjAvi.processIdMCEvent(
event,
{ id => setClientidBisObjFrom(editRef(), id) },
params = Map(RefEventParam.UseDiscreteAccess -> 0)
)
Если параметр не был передан при вызове метода-обработчика то проверка дискретного доступа осуществляется по настройке на административном объекте.
Параметр учитывается на всех методах-обработчиках (processRefEvent
, processHLEvent
, processIdMCEvent
и т.д.).
Проверка прав доступа на объект после его редактирования#
Открыть карточку
Адм. объекта
Установить признак
Дискретный доступ
Установить признак
Проверять изменение прав объектов на сохранении
При включенном дискретном доступе и проверке изменения прав на сохранении, если при редактировании пользователем объекта, объект становится недоступным даже для чтения, система выдаст ошибку с откатом изменений.
Деривация профилей#
Для дифференцирования доступа в разрезе требуемых организационных уровней (складов, бизнес-единиц, организаций и пр.), реализован следующий подход:
Роли разделяются на обычные и мастер-роли. Для мастер-ролей при назначении полномочий для бизнес-объектов появляется возможность указать, что значение параметров дискретного доступа определяется профилем доступа.
Профили доступа разделаются на мастер-профили, подчиненные и обычные. Мастер-роли можно включать только в мастер-профили доступа.
Для подчиненного профиля указываются конкретные значения параметров дискретного доступа.
При проверке прав пользователя система определяет параметры для дискретного доступа от параметров, указанных в подчиненном профиле.
Подправила#
Особенности подправил#
Для каждого правила можно создать подправило. Оно будет использовать те же параметры, что и основное правило, но при этом автоматически нигде не проверяется. Программист должен самостоятельно внедрить соответствующую проверку в коде. После этого администратор, зная имя подправила, сможет задать для него нужные ограничения и проверки.
Для администрируемого объекта можно создать подправило, выполнив следующие шаги:#
Открыть карточку администрируемого объекта.
Перейти в закладку Дискретные ограничения доступа.
Выбрать существующие правило или создать его.
Под выбранным или созданным правилом добавить подправило.
Назвать подправило именем, которое используется в коде для проверки доступа.
Задать необходимые скрипты для подправила.
После выполнения этих шагов подправило будет создано и применено в соответствии с заданными параметрами.
Пример проверки подправила в выборке:
override protected def onRefresh: Recs = {
lazy val sSubRule = "SomeSubRuleName"
val ropav = SomeApi().refreshByParent(getIdMaster)
if (needApplyDiscreteAccess) {
lazy val bHasGroup = Btk_AcLib().hasBtkGroup(acObject, acObject)
ropav.filter { rv =>
val rows = AcRows(Seq(rv))
val viewPriv = if (bHasGroup) Btk_AdminPkg.viewGroupPriv.get else Btk_AdminPkg.viewObjPriv.get
val editPriv = if (bHasGroup) Btk_AdminPkg.editGroupPriv.get else Btk_AdminPkg.editObjPriv.get
Btk_AdminPkg().hasObjPrivByRows(acObject.get, viewPriv, rows, spSubRule = sSubRule) ||
Btk_AdminPkg().hasObjPrivByRows(acObject.get, editPriv, rows, spSubRule = sSubRule)
}.map { rop =>
Row(rop.idJ, SomeApi().getHeadLine(rop.idJ))
}
} else {
ropav.map { rop =>
Row(rop.idJ, SomeApi().getHeadLine(rop.idJ))
}
}
}
Проверка полномочий в печатных формах#
Для реализации проверки доступа к объектам, которые получаются внутри запроса при построении печатных форм, необходимо использовать макросы в запросах.
При построении печатной формы эти макросы будут заменены на скрипты для фильтрации объектных привилегий. Фильтрация выполняется на основе:
Настроек административного объекта
Параметров, настроенных в ролях пользователя
В результате будут использованы только те объекты, на которые у пользователя есть права.
Важно: Если в печатной форме используется файл с запросом, где не прописаны необходимые макросы, но при этом:
На закладке настроен доступ к администрируемым объектам
В запросе есть обращение к соответствующей таблице
Тогда система не позволит пользователю без прав распечатать документ и выведет сообщение об ошибке с просьбой исправить запрос, добавив корректные макросы.
Настройка дискретного доступа в отчетах#
Для реализации проверки дискретного доступа в запросах отчетов необходимо выполнить следующие настройки:
В отчете
Rpt_Report
на закладке «Настройка контроля доступа к данным печатной формы» выбрать администрируемый объект для ограничения доступаУказать объектную привилегию (если не выбрана, будет проверяться привилегия «Доступ на чтение из печатных форм» -
viewReport#
)В поле «Список алиасов» указать алиасы таблиц через «;» (если поле пустое, по умолчанию используется «t»)
Для удобства система автоматически сгенерирует макросы для вставки в запрос печатной формы.
Использование макросов дискретного доступа в Jasper-отчетах#
В Jasper-отчетах макрос передается как параметр. Имя параметра формируется по шаблону:
DM_(<системное_имя_административного_объекта>)_(<алиас_таблицы>)
Чтобы использовать параметр как макрос для подстановки скрипта дискретного доступа, в запросе его нужно указать со знаком «!» (восклицательный знак).
Пример объявления параметра и использования в запросе для ограничения доступа к таблице Btk_User
:
<parameter name="DM_(BTK_USER)_(T)" class="java.lang.String">
<defaultValueExpression><![CDATA["1=2"]]></defaultValueExpression>
</parameter>
<queryString>
<![CDATA[select sHeadLine_dz
from btk_user t
where $P!{DM_(BTK_USER)_(T)}]]>
</queryString>
Рекомендация: Для тестирования отчета можно указать значение по умолчанию как «1=1», но перед загрузкой формы в приложение его следует убрать, чтобы избежать случайного предоставления полного доступа при ошибках в настройках.
Использование макросов дискретного доступа в отчетах типа «Текстовый», «Excel», «Word»#
В запросе необходимо указать макрос, который будет заменен скриптом дискретного доступа. Макрос формируется по шаблону:
&DM_(<системное_имя_административного_объекта>)_(<алиас_таблицы>)
Пример запроса для печатной формы типа «Word» с ограничением доступа к таблице Btk_User
:
[#&Order= select sHeadLine_dz, sMnemoCode_dz from BTK_USER t
where &DM_(Btk_User) #]
Примеры скриптов#
Примитивное правило по атрибуту класса#
Правило значимое, тип данных строка. Фильтрация по like атрибута sCode
Скрипт для фильтрации объектных привилегий:
select 1
from RplTst_ClassTree tt
join (
select cast(json_array_elements_text(cast((¶ms) as json)) as varchar) as sCode
) as codes
on tt.sCode like codes.sCode
where tt.id = (&id)
Скрипт проверки строк по объектному кешу:
for (p: params) {
if (row.sCode != null && p != null) {
if (like(row.sCode, p)) {
return true;
}
}
}
Примитивное правило по атрибуту коллекции#
Правило ссылочное на Btk_Group
. Фильтрация по прямому вхождению объекта в группу (фильтрация коллекции Btk_ObjectGroup
).
Скрипт для фильтрации объектных привилегий:
select 1
from Rpltst_TestGroup tt
join Btk_ObjectGroup og
on tt.gid = og.gidSrcObject
join (
select cast(json_array_elements_text(cast((¶ms) as json)) as int8) as id
) as params
on og.idGroup = params.id
where tt.id = (&id)
Скрипт проверки строк по объектному кешу:
for (p: params) {
var rops = toJRops(Btk_ObjectGroupApi.byParent(row.data())).asList();
if (p != null) {
for (r: rops) {
if (r.idGroup == p) {
return true;
}
}
}
}
Примитивное правило по адм. объекту, созданному не по классу (произвольная выборка)#
Правило значимое, тип данных строка. Фильтрация регистронезависимая по like поля sClass
Скрипт для фильтрации объектных привилегий:
select 1
from json_array_elements_text(cast((¶ms) as json)) as p
where upper((&sClass)) like upper(p)
Скрипт проверки строк по объектному кешу:
for (p: params) {
if (row.sClass != null && p != null) {
if (like(row.sClass.toUpperCase(), p.toUpperCase())) {
return true;
}
}
}
Составное правило по 3 атрибутам класса#
Параметры правила:
dDate
- Значимый, тип данных дата.nNumber
- Значимый, тип данных числоsCaption
- Значимый, тип данных строка
Фильтрация отбирает записи у которых дата меньше указанной, число равно указанному, и Наименование по like регистронезависимо совпадает с указанной
Скрипт для фильтрации объектных привилегий:
select 1
from RplTst_AllDbTypes tt
where tt.id = (&id)
and exists (
select 1
from (
select v -> 'dDate' as dDate
,v -> 'nNumber' as nNumber
,v -> 'sCaption' as sCaption
from json_array_elements(cast((¶ms) as json)
) as v
) p
where exists (
select 1
from json_array_elements_text(p.dDate) pp
where tt.dDate <= to_timestamp(pp, 'DD.MM.YYYY HH24:MI:SS')
)
and exists (
select 1
from json_array_elements_text(p.nNumber) pp
where tt.nNumber = cast(pp as numeric)
)
and exists (
select 1
from json_array_elements_text(p.sCaption) pp
where upper(tt.sCaption) like upper(pp)
)
)
Скрипт проверки строк по объектному кешу:
for (p: params) {
var jObj = toJObject(p);
var res = true
//проверка даты
if (res) {
res = false
var aValues = jObj.childJArray("dDate");
var i = 0
while(i < aValues.size() && !res) {
var value = aValues.getDate(i);
if (row.dDate <= value) {
res = true;
}
i = i + 1;
}
}
//проверка числа
if (res) {
res = false
var aValues = jObj.childJArray("nNumber");
var i = 0
while(i < aValues.size() && !res) {
var value = aValues.getNumber(i);
if (row.nNumber == value) {
res = true;
}
i = i + 1;
}
}
//проверка наименования
if (res) {
res = false
var aValues = jObj.childJArray("sCaption");
var i = 0
while(i < aValues.size() && !res) {
var value = aValues.getString(i);
if (row.sCaption != null && value != null) {
if (like(row.sCaption.toUpperCase(), value.toUpperCase())) {
res = true;
}
}
i = i + 1;
}
}
if (res) {
return true;
}
}
Составное правило по атрибуту класса и коллекции#
Параметры правила:
sCaption
- Значимый, тип данных строкаidGroup
- ссылочный наBtk_Group
Фильтрация отбирает записи, у которых Наименование по like регистронезависимо совпадает с указанным, и есть прямое вхождение в группу (фильтрация коллекции Btk_ObjectGroup
).
Скрипт для фильтрации объектных привилегий:
select 1
from RplTst_TestGroup tt
where tt.id = (&id)
and exists (
select 1
from (
select v -> 'sCaption' as sCaption
,v -> 'idGroup' as idGroup
from json_array_elements(cast((¶ms) as json)) as v
) p
where exists (
select 1
from json_array_elements_text(p.sCaption) pp
where upper(tt.sCaption) like upper(pp)
)
and exists (
select 1
from Btk_ObjectGroup og
join json_array_elements_text(p.idGroup) pp
on og.idGroup = cast(pp as int8)
where og.gidSrcObject = tt.gid
)
)
Скрипт проверки строк по объектному кешу:
for (p: params) {
var jObj = toJObject(p);
var res = true
//проверка наименования
if (res) {
res = false
var aValues = jObj.childJArray("sCaption");
var i = 0
while(i < aValues.size() && !res) {
var value = aValues.getString(i);
if (row.sCaption != null && value != null) {
if (like(row.sCaption.toUpperCase(), value.toUpperCase())) {
res = true;
}
}
i = i + 1;
}
}
//проверка группы
if (res) {
res = false
var rops = toJRops(Btk_ObjectGroupApi.byParent(row.data())).asList();
var aValues = jObj.childJArray("idGroup");
var i = 0
while(i < aValues.size() && !res) {
var value = aValues.getLong(i);
for (r: rops) {
if (r.idGroup == value) {
res = true;
}
}
i = i + 1;
}
}
if (res) {
return true;
}
}
Составное правило по 2 атрибутам адм. объекта, созданного не по классу (произвольная выборка)#
Параметры правила:
sClass
- Значимый, тип данных строкаidRefClass
- ссылочный наBtk_Class
Фильтрация отбирает записи, у которых поле sClass
по like регистронезависимо совпадает с указанным, и поле idRefClass
совпадает с указанным
Скрипт для фильтрации объектных привилегий:
select 1
from (
select v -> 'sClass' as sClass
,v -> 'idRefClass' as idRefClass
from json_array_elements(cast((¶ms) as json)) as v
) p
where exists (
select 1
from json_array_elements_text(p.sClass) pp
where upper((&sClass)) like upper(pp)
)
and exists (
select 1
from json_array_elements_text(p.idRefClass) pp
where (&idRefClass) = cast(pp as int8)
)
Скрипт проверки строк по объектному кешу:
for (p: params) {
var jObj = toJObject(p);
var res = true
//проверка sClass
if (res) {
res = false
var aValues = jObj.childJArray("sClass");
var i = 0
while(i < aValues.size() && !res) {
var value = aValues.getString(i);
if (row.sClass != null && value != null) {
if (like(row.sClass.toUpperCase(), value.toUpperCase())) {
res = true;
}
}
i = i + 1;
}
}
//проверка idRefClass
if (res) {
res = false
var aValues = jObj.childJArray("idRefClass");
var i = 0
while(i < aValues.size() && !res) {
var value = aValues.getLong(i);
if (row.idRefClass == value) {
res = true;
}
i = i + 1;
}
}
if (res) {
return true;
}
}