Управление доступом к строкам таблиц
В данном разделе содержится описание работы 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:
row_index, указанные вrangesв командеread-table, считают индексы относительно строк, записанных на диск, а не строк, доступных пользователю. Например, если запросить первые 100 строк (read-table //path/to/table[:#100]), при этом не имея доступа ни к одной из этих строк, вернётся пустой результат.- Аналогично, в операциях поверх таблиц, которые пользователь не может читать полностью,
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.
Тогда выполняется следующее:
-
Любой пользователь, у которого есть доступ
readна данную таблицу, по умолчанию не сможет прочитать ни одной строки. Однако, такой пользователь сможет прочитать атрибуты таблицы. -
Пользователь
usernameсможет прочитать только те строки, где колонкаuser_idравна12345(при условии, что у него также есть доступreadна таблицу целиком). -
Если пользователь попытается прочитать строки без флага
omit_inaccessible_rows, то возникнет ошибка авторизации.
Право полного чтения
Право full_read даёт возможность читать всю таблицу полностью, вне зависимости от других ACE с row_access_predicate. Такое право необходимо для некоторых действий, которые либо работают на уровне control plane, либо не интерпретируют (а соответственно, не распаковывают блоки данных) строчки. Такими действиями являются команды copy, concatenate, move, а также операция RemoteCopy.
Примечание: тривиальный предикат (выражение, всегда возвращающее true) не эквивалентен full_read. Поскольку в общем случае проверка доступа ко всем строкам требует прочтения всех строк, YTsaurus не делает никаких попыток проверить, является ли предикат тривиальным. Симметрично, при наличии всегда-ложного предиката, система будет вынуждена прочитать все строки таблицы и для каждой проверить, возвращает ли предикат false.
Замечания
Внимание
Установка строчной ACE ограничивает доступ к строкам для всех пользователей, не соответствующих предикату. Поэтому, прежде чем ограничивать доступ на уровне строк, убедитесь, что подобные действия не приведут к поломке процессов, работающих с таблицей.
- В текущей реализации функциональность распространяется лишь на чтение строк. Поэтому не нужно указывать в атрибуте
permissionsправа, отличные отread. - В предикате можно использовать только те колонки, которые есть в схеме.
- Построчные ACE наследуются стандартным образом, и их наследование, в частности, обрывается в тех узлах, где выставлено
inherit_acl = %false. - Если в эффективном ACL таблицы есть ACE, в котором выражение некорректно (например, ссылается на несуществующую колонку, возвращаемый тип отличается от boolean, складывает число и строку), то любая операция чтения будет возвращать ошибку. Для того, чтобы этого избежать, рекомендуется выставлять подобные ACE только в тех директориях, в которых все таблицы имеют гомогенную схему.
- Для того, чтобы протестировать предикат, можно воспользоваться операцией Merge с
input_query, поместив выражение после ключевого словаWHERE. Оба механизма используют одинаковый синтаксис для выражений, поэтому результат работы операции будет совпадать с тем, какие строки увидит пользователь, на которого действует соответствующий ACE. - Управление строчными ACE доступно только администраторам YTsaurus.