Выражения
В системе контроля доступа в формате ABAC (Attribute Based Access Control) для получения решения о доступе Разрешить/Запретить выполняется вычисление Политик. Кроме свойств основных элементов политик (Правило, Политика и Набор Политик) на результат вычисления влияют логические выражения, которые в этих элементах содержатся.
Сами логические выражения записываются в виде строк для упрощения описания элементов Политик. Это подразумевает реализацию небольшого языка, а следовательно и парсера, и логики вычисления этих выражений.
Поскольку контроль доступа требуется в разных компонентах, которые реализованы на разных ЯП, необходимо иметь несколько реализаций языка выражений (под каждый язык). Произвольно взятое выражение должно обрабатываться одинаково в любой реализации для корректного вычисления. Потому, в качестве источника истины необходима общая спецификация, на которую нужно ориентироваться при его реализации. Описанию этой спецификации и посвящена данная статья.
Грамматика
Ниже описаны лексемы, используемые в выражениях.
Лексема | Примеры | Описание |
---|---|---|
Строковые литералы | "строка" 'строка' "строка с \"экранированием\"" |
Строки в кавычках либо одинарных, либо в двойных.
|
Логические константы | true false |
Должны быть регистронезависимы. |
Null-литерал | null |
Должен быть регистронезависим. |
Имена | foo bar fizz_buzz FIZZ_BUZZ |
Предполагаются как имена функций или как ключи атрибута.
|
Точка | . |
Является разделителем ключей атрибута. |
Запятая | , |
Предназначена для перечисления элементов списков и аргументов при вызове функций. |
Круглые скобки | () |
Предназначены для вызова функции. |
Квадратные скобки | [] |
Предназначены для обозначения списков. |
Операторы сравнения | < , > , = , <= , >= != |
Бинарные операторы сравнения. |
Операторы включения | IN NOT IN |
Бинарные операторы включения/невключения (для проверки наличия или отсутствия значения в списке).
|
Ниже приведены составные синтаксические конструкции.
Название | Примеры | Описание |
---|---|---|
Обращение к атрибуту | subj subg.type |
Одно или несколько имён, разделённых точками |
Список | [] [1, 2, 3] ['foo', 'bar'] |
Перечисления значений через запятую, обёрнутые в квадратные скобки.
|
Вызов функции | not(false) length([]) intersects(subj.roles, ['role_a', 'role_b']) |
Имя функции и перечисление аргументов через запятую, обёрнутые в круглые скобки.
|
Условие | subj.type = 'user' length([1, 2, 3]) >= 3 subj.role in ['role_a', 'role_b'] |
Применяет бинарный оператор (оператора сравнения или включения) к паре значений.
|
Значением считается: литерал, обращение к атрибуту, список и вызов функции.
Для простоты, синтаксически, в качестве выражения может выступать либо одно условие, либо одно значение.
Модель данных, атрибуты
В языке присутствуют несколько атомарных типов данных:
-
integer
- целое число -
float
- вещественное число -
string
- символьные строки -
boolean
- константыtrue
иfalse
-
null
- константаnull
Также есть составной тип данных "список". Списки должны подчиняться следующим правилам:
-
Список не следует считать упорядоченным
-
Список может содержать значения разных типов
Язык выражений позволяет записать значения каждого из атомарных типов и списки из атомарных значений.
Значения атрибутов, используемых в выражениях, могут также иметь либо атомарный тип, либо список из значений атомарных типов. Однако в качестве значений атрибутов доступна еще пара типов, недоступных для записи в выражении: сущность и список сущностей.
Сущность может быть как обобщенной - известен только тип сущности, либо конкретной - известен как тип, так и некий уникальный идентификатор.
Сущность может быть использована, например, в качестве значения атрибутов субъекта и объекта доступа (например, subj = (тип="user", id=12)
), для указания некой связанной с субъектом сущности (например, subj.office = (тип="office", id=2)
) и т. п. Список сущностей может быть использован для указания множества сущностей, например, связанных с субъектом: subj.departments = [(тип="department", id=1), (тип="department", id=2)]
.
Для сущностей должны быть определены операторы проверки на равенство и неравенство: если у сущностей одинаковый тип и одинаковый не-null идентификатор, тогда это одна и та же сущность.
Правила сравнения и примеры выражений, в которых используется сущность можно посмотреть в секции Вычисление.
Вычисление
Операторы
Равенство/неравенство:
Проверки:
-
на равенство:
left = right
-
на неравенство:
left != right
Эти операторы должны быть определены для следующих вариантов сравнения:
-
(integer | float) = (integer | float)
– сравнение дробного с целым допустимо -
string = string
-
boolean = boolean
-
(list | integer | float | string | boolean | null) = null
– и наоборот -
сущность = сущность
– только для конкретных сущностей
Если пара значений не подходит под один из вариантов выше, результатом вычисления выражения должна быть ошибка типов.
Примеры сравнения значений:
Выражение | Результат вычисления |
---|---|
subj.type = 'user' |
true , если тип субъекта это user , иначе false |
subj.type = 42 |
Ошибка типа, т. к. значение атрибута соответствует типу string , а правый операнд - integer |
[] != null |
true , т. к. список это не null |
[1, 2] = [1, 2] |
Ошибка типа, т. к. сравнение списков не определено |
1 = true |
Ошибка типа, т. к. true это не то же самое, что число 1 |
Примеры сравнения сущностей:
subj | obj | Выражение | Результат вычисления |
---|---|---|---|
(тип="user", id=12) |
(тип="user", id=12) |
subj = obj |
true , т. к. тип и id сущностей совпадает |
(тип="user", id=12) |
(тип="department", id=12) |
subj = obj |
false , т. к. тип сущностей не совпадает |
(тип="user", id=12) |
(тип="user") |
subj = obj |
Ошибка типа, т. к. сравнение конкретной сущности с обобщенной не определено |
Сравнения порядка
Проверки:
-
value_a > value_b
-
value_a >= value_b
-
value_a < value_b
-
value_a <= value_b
Эти операторы должны быть определены только для integer
и float
. Сравнения integer
и float
, как и для проверки на равенство, допускаются.
Включение/невключение
Проверки:
-
atomic_value IN some_list
-
atomic_value NOT IN some_list
Левым операндом может быть значение любого атомарного типа или сущностью, правым операндом – список.
Оператор IN
возвращает true
, если список содержит указанное в значение. Оператор NOT IN
, наоборот, возвращает true
, если указанного в списке значения нет.
Проверка значений в списке на равенство должна выполняться согласно логике оператора =
, но ошибки типа должны игнорироваться, т. к. допускаются списки со значениями разного типа.
Примеры включения для списков значений:
Выражение | Результат вычисления |
---|---|
'foo' IN ['foo', 'bar'] |
true |
'foo' NOT IN [1, 2, 3, 'test'] |
true , т. к. строка foo отсутствует в списке |
Примеры проверки включения для списка сущностей; для subj.departments = [(тип="department", id=1), (тип="department", id=2)]
и obj = (тип="department", id=1)
:
Выражение | Результат вычисления |
---|---|
obj IN subj.departments |
true , т. к. в отдел с id=1 присутствует в списке |
1 IN subj.departments |
false , т. к. в subj.departments нет числа 1 |
Функции
Секция определяет набор функций, доступных для использования в выражениях.
Функция имеет имя и сигнатуру. Сигнатура описывает количество принимаемых аргументов, их тип и тип результата.
Если типы передаваемых аргументов не совпадают с указанными, результатом вычисления должна быть Ошибка типов.
В таблице ниже предоставлены реализованные функции:
Имя | Сигнатура | Примеры | Описание |
---|---|---|---|
not |
(boolean) -> boolean |
not(false) -> true not([1, 2, 3]) -> ошибка |
Инвертирует булево значение |
length |
(list) -> integer |
length([]) -> 0 length(['a', 'b', 'c']) -> 3 length('string') -> ошибка |
Возвращает количество элементов в списке |
intersects |
(list, list) -> boolean |
intersects(['a', 'b'], ['b', 'c']) -> true intersects([], ['a', 'b', 'c']) -> false intersects(['a', 'b'], 'ab') -> ошибка |
Возвращает true , если пара списков содержит хотя бы одно одинаковое значение.Проверка на равенство значений должна выполняться по тем же правилам, что и для оператора = . |
Выражения
В качестве выражения может выступать либо условие, либо значение. Значение это литерал, список или вызов функции. Но результатом вычисления выражения должно быть значение типа boolean
. Потому, если конструкция, выступающая в роли выражения, возвращает что-то отличное от true
или false
, результатом должна быть Ошибка типа.
Примеры выражений:
Выражение | Результат | Причина |
---|---|---|
true |
true |
- |
1 |
Ошибка типа | 1 - это не true |
'string' |
Ошибка типа | string - это не boolean |
'string' != '' |
true |
Проверка на неравенство возвращает true |
[1, 2, 3] |
Ошибка типа | Список - это не boolean |
length([1, 2, 3]) |
Ошибка типа | length возвращает integer |
length([1, 2, 3]) > 0 |
true |
Сравнение integer > integer здесь вернет true |
obj.some_number |
Ошибка типа | Указанный атрибут - это число |
obj.is_deleted |
false |
Указанный атрибут - это boolean (со значением false ) |