Перейти к содержанию

Выражения

В системе контроля доступа в формате ABAC (Attribute Based Access Control) для получения решения о доступе Разрешить/Запретить выполняется вычисление Политик. Кроме свойств основных элементов политик (Правило, Политика и Набор Политик) на результат вычисления влияют логические выражения, которые в этих элементах содержатся.

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

Поскольку контроль доступа требуется в разных компонентах, которые реализованы на разных ЯП, необходимо иметь несколько реализаций языка выражений (под каждый язык). Произвольно взятое выражение должно обрабатываться одинаково в любой реализации для корректного вычисления. Потому, в качестве источника истины необходима общая спецификация, на которую нужно ориентироваться при его реализации. Описанию этой спецификации и посвящена данная статья.

Грамматика

Ниже описаны лексемы, используемые в выражениях.

Лексема Примеры Описание
Строковые литералы "строка"

'строка'

"строка с \"экранированием\""
Строки в кавычках либо одинарных, либо в двойных.
  • Смешанные кавычки ('строка") запрещены
  • Двойные кавычки внутри одинарных (и наоборот) должны интерпретироваться как обычный символ
  • Экранирование через backslash доступно только для кавычек, с которыми записана строка: либо "\"строка\"", либо '\'строка\'', но не "\'строка\'"
  • В остальных случаях backslash запрещен
Логические константы true

false
Должны быть регистронезависимы.
Null-литерал null Должен быть регистронезависим.
Имена foo

bar

fizz_buzz

FIZZ_BUZZ
Предполагаются как имена функций или как ключи атрибута.
  • Должны обрабатываться как регистронезависимые
  • Допустимые символы: A-Z, a-z и нижнее подчеркивание
Точка . Является разделителем ключей атрибута.
Запятая , Предназначена для перечисления элементов списков и аргументов при вызове функций.
Круглые скобки () Предназначены для вызова функции.
Квадратные скобки [] Предназначены для обозначения списков.
Операторы сравнения <, >, =, <=, >= != Бинарные операторы сравнения.
Операторы включения IN

NOT 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)