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

В данном разделе содержится описание работы Row-Level Security (RLS), приводятся примеры использования.

Краткое описание

Row-Level Security (RLS) позволяет уточнить правила доступа к отдельным строкам таблицы на основе предикатов, вычисляемых на данных строк. Это позволяет хранить в одной таблице как общедоступные данные, так и приватные и определять доступы через произвольные выражения на языке запросов YTsaurus.

Принцип работы

Общие принципы работы системы контроля доступа в системе YTsaurus описаны в разделе Контроль доступа.

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

Выражение row_access_predicate должно вычисляться в булево значение. Выражение может ссылаться на любые колонки из схемы таблицы и использовать стандартные операторы и функции.

Предположим, что система выполняет проверку прав доступа на чтение к таблице T от имени пользователя U. Подобные чтения происходят при выполнении команды read-table, а также при подаче таблицы T в качестве входной в операцию MapReduce или другие.

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

Последнее свойство проверяется следующим образом: построим эффективный ACL таблицы T. Из составляющих его ACE отберем те, которые являются строчными (содержат атрибут row_access_predicate) и применимы к пользователю U (U либо явно упомянут в subjects, либо принадлежит к одной из групп, упомянутых в subjects). Обозначим результат через L.

В случае, если L пусто, это означает, что для таблицы T и пользователя U никакие строчные разрешения не действуют, и пользователь не сможет прочитать ни одной строки (за исключением случая, когда у него есть право full_read).

В противном случае, если нашлась хотя бы одна подходящая ACE, для каждой строки в таблице хотя бы один предикат из L должен вычисляться в true, чтобы пользователь мог прочитать эту строку.

Режим совместимости

Если установить на таблицу строчный ACL, то у пользователей, у которых нет права full_read, возникнет ошибка авторизации при попытке чтения таблицы.

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

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

Ограничения и особенности

RLS не поддерживается для динамических таблиц. Любая попытка чтения будет возвращать ошибку.

В силу динамичности правил доступа, не всегда система может эффективно выполнить те или иные действия. Например, количество данных, прочитанных с диска, не всегда пропорционально числу доступных строк. Но динамичность влияет не только на скорость, но и на некоторые API:

  1. row_index, указанные в ranges в команде read-table, считают индексы относительно строк, записанных на диск, а не строк, доступных пользователю. Например, если запросить первые 100 строк (read-table //path/to/table[:#100]), при этом не имея доступа ни к одной из этих строк, вернётся пустой результат.
  2. Аналогично, в операциях поверх таблиц, которые пользователь не может читать полностью, row_index указывать нельзя.

Выражение, указываемое в row_access_predicate, должно быть выражением, зависящим исключительно от значений в строке. В нём нельзя использовать JOIN, GROUP BY, текущего пользователя. Также нельзя использовать QL UDFs.

Утечка информации через метаданные

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

Кроме того, даже если ограничений на колонки нет, по метаданным всё равно можно получить информацию, которая может оказаться секретной.

Разберём игрушечный пример:
Пусть в таблице лежат две строчки и есть такой ACE:

{
    action = allow;
    subjects = [vasya];
    permissions = [read];
    row_access_predicate = "region != 'RU' or income < 1000";
}

Вася читает таблицу и видит лишь одну строчку. Из этого можно сделать вывод, что для оставшейся строчки предикат не выполняется, а значит для неё выполняется region = 'RU' and income >= 1000. Является ли эта информация секретной и возможна ли вообще (пример намеренно игрушечный, в реальных условиях в таблице скорее всего будет лежать больше строк), остаётся на усмотрение владельца данных.

Пример

Пусть на таблице установлен ACL, в котором есть следующая ACE:

{
    action = allow;
    subjects = [username];
    permissions = [read];
    row_access_predicate = "user_id = 12345";
}

Будем также считать, что на всем пути вверх до корня от данной таблицы более нет никаких строчных ACE.

Тогда выполняется следующее:

  1. Любой пользователь, у которого есть доступ read на данную таблицу, по умолчанию не сможет прочитать ни одной строки. Однако, такой пользователь сможет прочитать атрибуты таблицы.

  2. Пользователь username сможет прочитать только те строки, где колонка user_id равна 12345 (при условии, что у него также есть доступ read на таблицу целиком).

  3. Если пользователь попытается прочитать строки без флага omit_inaccessible_rows, то возникнет ошибка авторизации.

Право полного чтения

Право full_read даёт возможность читать всю таблицу полностью, вне зависимости от других ACE с row_access_predicate. Такое право необходимо для некоторых действий, которые либо работают на уровне control plane, либо не интерпретируют (а соответственно, не распаковывают блоки данных) строчки. Такими действиями являются команды copy, concatenate, move, а также операция RemoteCopy.

Примечание: тривиальный предикат (выражение, всегда возвращающее true) не эквивалентен full_read. Поскольку в общем случае проверка доступа ко всем строкам требует прочтения всех строк, YTsaurus не делает никаких попыток проверить, является ли предикат тривиальным. Симметрично, при наличии всегда-ложного предиката, система будет вынуждена прочитать все строки таблицы и для каждой проверить, возвращает ли предикат false.

Замечания

Внимание

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

  1. В текущей реализации функциональность распространяется лишь на чтение строк. Поэтому не нужно указывать в атрибуте permissions права, отличные от read.
  2. В предикате можно использовать только те колонки, которые есть в схеме.
  3. Построчные ACE наследуются стандартным образом, и их наследование, в частности, обрывается в тех узлах, где выставлено inherit_acl = %false.
  4. Если в эффективном ACL таблицы есть ACE, в котором выражение некорректно (например, ссылается на несуществующую колонку, возвращаемый тип отличается от boolean, складывает число и строку), то любая операция чтения будет возвращать ошибку. Для того, чтобы этого избежать, рекомендуется выставлять подобные ACE только в тех директориях, в которых все таблицы имеют гомогенную схему.
  5. Для того, чтобы протестировать предикат, можно воспользоваться операцией Merge с input_query, поместив выражение после ключевого слова WHERE. Оба механизма используют одинаковый синтаксис для выражений, поэтому результат работы операции будет совпадать с тем, какие строки увидит пользователь, на которого действует соответствующий ACE.
  6. Управление строчными ACE доступно только администраторам YTsaurus.