Обновление наших рекомендаций по разрешениям: grants как конфигурации в dbt Core v1.2
Если вам нужно было предоставить доступ к модели dbt с 2019 года по сегодняшний день, есть большая вероятность, что вы наткнулись на пост "Точные операторы grant, которые мы используем в проекте dbt" на Discourse. В нем объяснялись варианты для покрытия двух дополнительных возможностей:
- запрос отношений через привилегию "select"
- использование схемы, в которой находятся эти отношения, через привилегию "usage"
Решение тогда
До dbt Core v1.2 мы предлагали три возможных подхода (каждый из которых имел предостережения и компромиссы):
- Использование хуков
on-run-endдляgrant select on allтаблиц/представлений, которые только что построил dbt - Использование
post-hookдля предоставленияselectна модели сразу после ее построения - Использование либо стандартных грантов (будущие гранты в Snowflake), либо комбинации
post-hooksиon-run-endхуков
Эти варианты были передовыми... до сегодняшнего дня!
Что изменилось?
В версии v1.2 мы представили конфигурацию grants, которая работает очень похоже на post-hook, с двумя ключевыми отличиями:
- Вы настраиваете
grantsкак структурированный словарь, а не пишете весь SQL самостоятельно - dbt выберет наиболее эффективный путь для применения этих грантов
Почему grants лучше, чем хуки
Во-первых, хуки сложны! Особенно эта путаница с вложенными фигурными скобками.
Проблема тогда
Предположим, вы работали над инкрементальной моделью. Ранее вы предоставили доступ к этой инкрементальной модели напрямую reporter, чтобы люди могли запрашивать ее далее:
-- models/my_incremental_model.sql
{{ config(
materialized = 'incremental',
post_hook = ["grant select on {{ this }} to reporter"]
) }}
select ...
Со временем эта модель взяла на себя все больше и больше обязанностей, и вы решили переработать инкрементальную модель, чтобы она питала серию специализированных представлений. Заботливо, вы также удалили post_hook, который предоставлял прямой доступ к инкрементальной модели:
-- models/my_incremental_model.sql
{{ config(materialized = 'incremental') }}
select ...
Проблема? Пока вы не выполните --full-refresh, ваша инкрементальная модель все еще предоставлена роли reporter!
Решение сегодня
Новая реализация grants в dbt учитывает это. Она знает, переносятся ли гранты, когда модель запускается повторно, в зависимости от ее материализации и вашей базы данных. Она компенсирует разницу между существующими грантами и теми, которые вы действительно хотите.
Попробуйте!
-- models/my_incremental_model.sql
{{ config(
materialized = 'incremental',
grants = {'select': ['another_user']}
) }}
select ...
Запустите это, убедитесь, что another_user может выбрать вашу модель. Затем измените вашу модель и запустите ее снова:
-- models/my_incremental_model.sql
{{ config(
materialized = 'incremental',
grants = {'select': []}
) }}
select ...
Если вы проверите свою базу данных, вы должны увидеть, что никто не может выбрать из инкрементальной модели. Вы также можете увидеть в журналах уровня отладки, что dbt выполнил оператор revoke.
(Обратите внимание, что если grants отсутствует или установлен в {}, dbt поймет, что вы не хотите, чтобы он управлял грантами для этой таблицы. Поэтому лучше явно указать привилегию и то, что вы хотите, чтобы никто ее не имел!)
Отлично! Теперь, когда вы используете функцию grants в dbt v1.2, вы только что уделили этому больше внимания, чем когда-либо потребуется снова 😎
Есть ли еще место для хуков?
Да, конечно! Некоторые области, которые выделяются:
- Предоставление разрешений на другие типы объектов, такие как предоставление использования на схему
- Расширенные разрешения, такие как доступ на уровне строк
Предоставление разрешений на другие типы объектов
На данный момент все еще необходимо предоставлять usage на схемы пользователям, которым нужно будет выбирать объекты в этих схемах. Хотя dbt создает схемы в начале запусков, на самом деле нет способа настроить схемы как их собственные объекты в dbt.
Вот несколько способов, как вы можете подойти к этому:
- Вариант A -- простой и знакомый -- хуки на помощь
- Вариант B -- слишком хитро -- использовать граф dbt, чтобы определить, какие схемы нуждаются в "usage"
Вариант A: простой и знакомый
on-run-end:
# лучше как макрос
- "{% for schema in schemas %}grant usage on schema {{ schema }} to reporter;{% endfor %}"
Плюсы: Коротко, ясно, по делу.
Минусы: нам нужно повторить тот же список ролей здесь, который мы указали в нашей конфигурации grants.
Вариант B: Слишком хитро
Теперь, когда grants является реальной конфигурацией в dbt, доступной через метаданные dbt, вы можете делать с ней всевозможные интересные вещи. Например, определить, какие схемы имеют хотя бы один объект, предоставляющий select роли, и затем предоставить usage на эту схему этой роли!
-- macros/operations/reporting_grants.sql
{% macro grant_usage_on_schemas_where_select() %}
/*
Примечание: Это только псевдокод, для демонстрационных целей
Для каждой роли, которая может получить доступ хотя бы к одному объекту в схеме,
предоставить 'usage' на эту схему этой роли.
Таким образом, пользователи с этой ролью могут выполнять метаданные запросы, показывающие объекты
в этой схеме (обычная потребность для BI-инструментов)
*/
{% set schema_grants = {} %}
{% if execute %}
{% for node in graph.nodes.values() %}
{% set grants = node.config.get('grants') %}
{% set select_roles = grants['select'] if grants else [] %}
{% if select_roles %}
{% set database_schema = node.database ~ "." ~ node.schema %}
{% if database_schema in database_schemas %}
{% do schema_grants[database_schema].add(select_roles) %}
{% else %}
{% do schema_grants.update({database_schema: set(select_roles)}) %}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% set grant_list %}
{% for schema in schema_grants %}
{% for role in schema_grants[schema] %}
grant usage on schema {{ schema }} to {{ role }};
{% endfor %}
{% endfor %}
{% endset %}
{{ return(grant_list) }}
{% endmacro %}
Это, конечно, слишком хитро -- но вы поняли идею и получили иллюстрацию того, что возможно!
Вы даже можете сделать это в начале запуска, сразу после того, как dbt создаст свои схемы, а не ждать до конца. (Хотя это не большая проблема ждать.)
on-run-start:
- {{ grant_usage_on_schemas_where_select() }}
Расширенные разрешения (или другие операции)
Хотите ограничить доступ к определенным строкам в таблице для определенных пользователей? Или динамически маскировать значения столбцов в зависимости от того, кто запрашивает?
Подход варьируется в зависимости от базы данных: в Snowflake вам все еще понадобится post-hook, чтобы применить политику доступа к строкам или политику маскирования столбцов к вашей таблице, тогда как в Databricks вы бы использовали функции динамического представления.
Хорошо иметь хуки и операции как метод использования передовых возможностей базы данных. Любые случаи, которые станут широко и явно продемонстрированной потребностью, могут быть улучшены путем их интеграции в dbt-core.
Приложение
Предостережения и компромиссы оригинальных рекомендаций
on-run-end хуки:
в период времени между запуском модели и концом запуска никто не сможет запросить эту модель, вместо этого они получат ошибку "permission denied". Это создает простой в вашем BI-инструменте."
привилегия manage grants:
Стоит отметить, что эта привилегия является глобальной привилегией – теперь любой, использующий роль
transformer, может изменять гранты на любом объекте, как будто они являются владельцем объекта. Решать вам, комфортно ли вам с этим! Если нет, вы можете использовать комбинациюpost-hooksиon-run-endхуков вместо этого 🙂"
Основные проблемы:
- Даже если вы написали самый DRY код, который могли, все равно существуют тысячи проектов, которые все написали одни и те же DCL операторы, обернутые в одни и те же макросы.
- Стандартные + будущие гранты — наша оригинальная рекомендация, еще в 2019 году — сложны. Они часто требуют дополнительных разрешений (статус суперпользователя!), они вступают в силу автоматически и не подходят для многих организаций с более строгими политиками безопасности.
Проблемы, связанные с хуками
Это всего лишь образец проблем, которые мы видели:
- Post hooks, которые вызывают макросы, парсятся с execute = False #2370
- get_relation возвращает none в контексте хука #2938
- this.is_view и this.is_table не работают в BigQuery внутри хука #3529
- пользовательский путь схемы таблицы {{ this }} неправильно парсится в post-hook макросе #3985
- Post-hook не разрешает пользовательскую схему #4023
- [CT-80] [Bug] post-hook макрос генерирует SQL с неправильной исходной таблицей #4606


Comments