- PVSM.RU - https://www.pvsm.ru -
Эта статья реализована в стиле HOWTO, поэтому не будет долгого вступления. Предполагается, что люди, нуждающиеся в механизме CLS, уже знают многие базовые вещи (поэтому каждую сущность разжевывать не будем - ссылки на самостоятельное изучение будут оставлены). Цель статьи - показать способ реализации ролевого доступа к колонкам при визуализации в Apache Superset. Итак, поехали.
Многие, наверняка, слышали про RLS (Row Level Security) в Apache Superset [1] - механизм управления доступом к строкам данных. Например, можно запретить пользователю с ролью, отличной от admin, использовать определенные строки данных датасета.
К сожалению, механизма CLS из коробки нет. И реализовать его полностью аналогично RLS не получится (на уровне датасета нельзя внедрить CLS правило, чтобы оно применялось ко всем чартам, построенным на датасете). Но его можно реализовать на уровне чарта. В этом поможет связка Jinja [2]+ Handlebars [3].
Представим задачу для примера. У нас есть таблица salary, имеющая следующее наполнение (прошу прощения за employee_id=1 - забыл поменять, под капотом ClickHouse, но это и не важно в рамках статьи)

И нам необходимо реализовать чарт типа TABLE таким образом, чтобы колонка employee_salary отображалась только пользователю с именем admin (выбирайте того пользователя, из под которого будете проделывать все манипуляции; в примере все будет сделано из под пользователя admin - это важно).
Первое, что будет необходимо сделать - убедиться, что активирован feature flag [4], позволяющий использовать Jinja. Для этого необходимо проверить, что в файле superset/docker/pythonpath_dev/superset_config.py в словаре FEATURE_FLAGS ключ ENABLE_TEMPLATE_PROCESSING имеет значение True.
FEATURE_FLAGS = {"ENABLE_TEMPLATE_PROCESSING": True ...}
Если такого элемента словаря не было - добавляем и делаем рестарт (здесь все зависит от типа инсталляции).
Затем нужно на основе таблицы salary создать физический датасет (так проще, но можно и виртуальный, позже станет понятно, что разницы нет).
Далее необходимо получить информацию об имени текущего пользователя и добавить её в датасет как вычисляемую колонку. В этом поможет макрос Jinja current_username()(список всех макросов [5]). Не будем углубляться в получасовую теорию — сейчас всё станет интуитивно понятно. Делаем точь-в-точь, как показано на изображении.

Макрос под капотом - код на языке Python, который возвращает результат строковым типом. Однако в SQL-контексте это значение интерпретируется как обычное слово. Чтобы явно преобразовать его в SQL-строковый литерал, необходимо заключить результат в одинарные кавычки. Название колонки может быть произвольным — в нашем примере используется 'username'.
Для проверки создадим простой TABLE-чарт, отобразив колонку username. Результат будет соответствовать ожиданиям:

Отлично! Мы уже добились того, что датасет содержит колонку с информацией о текущем пользователе. Следующая задача - в зависимости от значения в колонке username скрывать/отображать колонку employee_salary. Для реализации этой функциональности будем использовать тип визуализации Handlebars. Выбираем его для текущего чарта.
Далее на чарте нажимаем кнопку UPDATE CHART, переключаемся на вкладку CUSTOMIZE и получаем следующую картину

Итак, кратко и по порядку (на примере будет яснее).
Handlebars Template [6] - шаблонизатор для генерации динамического кода HTML. Под капотом имеет множество хелперов [7] (логические операции)
CSS Styles - пишем свой CSS для визуализации
Давайте для простоты сделаем таблицу с минимальными изменениями CSS. Готовый код Handlebars Template с комментариями:
<table class="data-table">
<!-- thead - названия колонок -->
<thead>
<tr>
<th>ID сотрудника</th>
<th>Имя сотрудника</th>
<!-- если пользователь не admin, то ячейки с названием колонки не будет -->
<!-- data - объект, содержащий информацию из датасета. берем значение колонки username из 0 элемента (первая строка) -->
{{#if (eq data.0.username "admin")}}
<th>Зарплата</th>
{{/if}}
</tr>
</thead>
<tbody>
<!-- #each data - итерируемся по объекту data, фактически, построчный вывод таблицы -->
{{#each data}}
<tr>
<!-- this - контекстный объект, текущая строка -->
<td> {{this.employee_id}}</td>
<td> {{this.employee_name}}</td>
<!-- если пользователь не admin, то ячейку employee_salary не отображаем-->
{{#if (eq this.username "admin")}}
<td> {{this.employee_salary}} </td>
{{/if}}
</tr>
{{/each}}
</tbody>
</table>
Готовый код CSS Styles
.data-table {
border-collapse: collapse;
width: 100%;
}
.data-table th, .data-table td {
border: 1px solid black;
padding: 8px;
text-align: left;
}
Теперь проверим работоспособность решения. Поскольку все манипуляции выполнялись от имени пользователя admin (напомню, у вас может быть другой пользователь - используйте его имя в коде вместо admin), для проверки достаточно изменить условия отображения.
Например, замените в коде Handlebars Template 'admin' на 'dsa' (или любой произвольный набор символов). Вот что мы получим в результате:

Изображение выше подтверждает, что мы успешно реализовали Column-Level Security (CLS) на уровне чарта. Дальнейшие действия просты:
Возвращаем в коде Handlebars Template значение 'dsa' обратно на 'admin'
Сохраняем изменения в чарте
Добавляем чарт на дашборд
Тестируем под разными учётными записями (admin и обычный пользователь)
Проверяем корректность работы механизма
Итого: Комбинация Jinja + Handlebars + CSS предоставляет весьма богатые возможности кастомизации.
Пользуйтесь на здоровье!
Автор: select_zvezdo4ka_from
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/apache-2/426550
Ссылки в тексте:
[1] RLS (Row Level Security) в Apache Superset: https://superset.apache.org/docs/security/#row-level-security
[2] Jinja : https://preset.io/blog/intro-jinja-templating-apache-superset/
[3] Handlebars: https://preset.io/blog/introduction-to-handlebars-charts-in-preset/
[4] feature flag: https://superset.apache.org/docs/configuration/configuring-superset#feature-flags
[5] список всех макросов: https://superset.apache.org/docs/configuration/sql-templating/#available-macros
[6] Handlebars Template: https://handlebarsjs.com/guide/
[7] хелперов: https://www.npmjs.com/package/just-handlebars-helpers#helpers
[8] Источник: https://habr.com/ru/articles/932184/?utm_source=habrahabr&utm_medium=rss&utm_campaign=932184
Нажмите здесь для печати.