Динамический виджет-навигатор с RLS в AW BI
О чём эта статья. Практический кейс: настраиваем виджет с кнопками дашбордов, который у каждого пользователя выглядит по-своему — показывает только то, что ему доступно. Это вторая часть. В первой части мы разобрали, как устроена система прав доступа и RLS в AW BI.
Описание кейса
Делаем виджет-навигатор, который показывает каждому пользователю только те дашборды, к которым у него есть доступ. Кнопки формируются динамически из данных модели. Наш пример носит теоретический характер и число дашбордов, как и пользователей с разными уровнями доступа в примере не так много, но этот случай легко масштабируется на создание больших навигационных панелей для компании.
Группы пользователей и их доступ:
Группа (department) |
Кто это | Видит дашборды |
|---|---|---|
hr |
HR-отдел | Показатели HR |
sales |
Коммерческий отдел | Продажи |
warehouse |
Складской отдел | Динамика поступления на склады |
all |
Руководство | Все три дашборда |
Тестовые пользователи:
| login | department | Что увидит в виджете |
|---|---|---|
| test_user | hr | Только кнопку «Показатели HR» |
| a.khoroshilova | sales | Только кнопку «Продажи» |
| s.studenikin | all | Все три кнопки |
Шаг 1 — Подготовка данных
Нам нужны два Excel-файла, которые станут источниками данных для двух моделей.
Файл 1: user_permissions.xlsx
Таблица с логинами пользователей и их атрибутом department. По этим данным система будет понимать, кто из какого отдела.
Эти данные могут приходить из внешней базы данных, динамически. В рамках демонстрации мы упростили случай до получения данных из excel документа.
| login | department |
|---|---|
| test_user | hr |
| a.khoroshilova | sales |
| s.studenikin | all |
Логины в колонке
loginдолжны совпадать с логинами учётных записей в AW BI один в один. Регистр важен: «Ivanov» и «ivanov» — это разные значения для системы.
Файл 2: dashboard_catalog.xlsx
Каталог дашбордов с привязкой к департаменту. Именно из этой таблицы виджет возьмёт список кнопок для каждого пользователя. При масштабировании кейса это упростит в будущем добавление любого нового дашборда в список доступных, сразу с нужной категорией доступа.
| department | dashboard_name | dashboard_url |
|---|---|---|
| hr | Показатели HR | https://aw-demo.ru/public/dashboard/HEMSqKCrerM2ZEMiFCMUGtHH527XoVIN |
| sales | Продажи | https://aw-demo.ru/app/dashboard/1140/view/3758 |
| warehouse | Динамика поступления на склады | https://aw-demo.ru/share/dashboard/vK4dkvM7QfLptk7OHARtsZnBmYmowb4LK |
| all | Показатели HR | https://aw-demo.ru/public/dashboard/HEMSqKCrerM2ZEMiFCMUGtHH527XoVIN |
| all | Продажи | https://aw-demo.ru/app/dashboard/1140/view/3758 |
| all | Динамика поступления на склады | https://aw-demo.ru/share/dashboard/vK4dkvM7QfLptk7OHARtsZnBmYmowb4LK |
Строки с
department = all— это записи для руководства. Директор видит все дашборды, потому что в его атрибуте стоитall, а в таблице есть строки сall. Никаких специальных исключений в правилах прописывать не нужно — логика реализована через данные.
Шаг 2 — Настройка схемы доступов
Прежде чем использовать атрибут department в правилах — его нужно зарегистрировать в схеме доступов. Без этого шага атрибут не появится в выпадающем списке при настройке правил.
- Перейдите в Администрирование → Схемы доступов
- Нажмите «Добавить»
- Заполните форму:
- Наименование:
department - Алиас:
Департамент - Тип: Строка
- Галочку «Участвует в идентификации пользователя» не ставим
- Наименование:
- Нажмите «Сохранить»
Шаг 3 — Модель user_permissions
user_permissions — встроенная модель AW BI. Создавать её не нужно, она уже есть в системе. Наша задача — наполнить её данными из подготовленного файла.
- Перейдите в раздел Модели (не забываем, что под учеткой технического администратора стенда)
- Найдите модель user_permissions (встроенная, есть у всех)
- Войдите в режим редактирования
- Добавьте источник данных — файл
user_permissions.xlsx - Нажмите «Загрузить данные в хранилище»
Шаг 4 — Маппинг провайдера
Данные загружены в user_permissions, но система ещё не знает, что поле department из этой модели — это тот самый атрибут department из схемы доступов. Эту связь устанавливает маппинг провайдера.
- Перейдите в Администрирование → Провайдеры
- Откройте внутренний провайдер AW (тип:
AW user_permissions) - Перейдите на вкладку «Маппинг схемы»
- Найдите строку с атрибутом
department(левый столбец — атрибуты схемы) - В правом столбце введите:
department(имя поля из моделиuser_permissions) - Нажмите «Сохранить»
Если маппинг не настроен — атрибут
departmentне дойдёт до пользователя, и правила фильтрации будут работать вхолостую. Это самая частая причина пустых виджетов при внешне правильной настройке.
Шаг 5 — Модель dashboard_catalog и правило доступа
Теперь нам осталось буквально несколько шагов для работы нашей магии предварительных настроек. Делаем ту самую модель, в которой будет настроен доступ к строкам модели, на основе правил доступа.
Создаём модель
- Перейдите в раздел Модели → Добавить
- Выберите тип модели (в нашем случае логическая)
- Добавьте источник данных — файл
dashboard_catalog.xlsx - Отметьте поле dashboard_url как ссылку (ниже как это сделать и зачем необходимо)
- Загрузите данные в хранилище
Для чего помечаем поле dashboard_url как ссылку
Это важный шаг. Без него URL будет просто текстом, и навигация из виджета не заработает у нас должным образом.
- В режиме редактирования модели нажмите на поле dashboard_url
- Включите опцию «Является ссылкой»
Создаём правило доступа к строкам
- В редакторе модели
dashboard_catalogнажмите кнопку «Настройки» - Перейдите в раздел «Правила доступа»
- Нажмите «Создать правило»
- В блоке «Ограничения по данным (по полю модели)» настройте условие:
- Поле модели:
department - Тип: Строка
- Оператор:
= - Вид сравнения: Атрибут (не «Значение»!)
- Атрибут: Департамент
- Поле модели:
- Нажмите «Сохранить»
Вид сравнения «Атрибут» — ключевой момент. Это значит, что система будет сравнивать поле
departmentкаждой строки с атрибутомdepartmentконкретного пользователя динамически. Если выбрать «Значение» — система будет сравнивать с фиксированным текстом, и для всех будет одно и то же условие.
Раздел «Ограничения по пользователям» в нашем кейсе оставляем пустым. Правило применяется ко всем, а логика для руководства уже заложена в данных: строки с department = all отображаются пользователям с атрибутом all.
Шаг 6 — HTML-виджет
Теперь остался завершающий этап — сделать динамическое отображение наших изменяемых данных.
HTML-виджет с кодом ниже в данном случае — это лишь пример свободной интерпретации наших данных, показанный в рамках демонстрации примера. Ваши кнопки, логика отображения тут может быть абсолютно любой. Все упирается лишь в ваши навыки создания кастомных html-визуализаций.
Создаём виджет и добавляем поля
- Перейдите в Виджеты → Добавить → HTML-виджет
- Выберите модель
dashboard_catalogкак источник данных - В настройках полей добавьте в «Столбцы» (без агрегации, плоская таблица):
dashboard_namedashboard_url
- Вставьте код из трёх вкладок ниже
- Нажмите «Выполнить» для предпросмотра, затем «Опубликовать»
Вкладка HTML
<div class="nav-container">
<div class="nav-header">
<div class="nav-logo">
<span class="logo-aw">AW</span><span class="logo-bi">BI</span>
</div>
<div class="nav-title">Мои дашборды</div>
<div class="nav-subtitle">Доступные вам отчёты</div>
</div>
<div class="btn-list" id="btn-list"></div>
</div>
Вкладка CSS
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
* { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--pink: #e8445a;
--teal: #2ec4b6;
--bg: #f7f9fc;
--card: #ffffff;
--text: #1a1a2e;
--subtext: #6b7280;
--border: #eaecf0;
}
.nav-container {
width: 100%; height: 100%;
display: flex; flex-direction: column; gap: 16px;
padding: 20px; background-color: var(--bg);
border-radius: 16px; font-family: 'Inter', sans-serif;
overflow-y: auto;
}
.nav-header {
display: flex; flex-direction: column; gap: 4px;
padding-bottom: 14px;
border-bottom: 2px solid var(--border);
}
.nav-logo { display: flex; margin-bottom: 8px; }
.logo-aw { font-size: 1.3rem; font-weight: 700; color: var(--pink); }
.logo-bi { font-size: 1.3rem; font-weight: 700; color: var(--teal); }
.nav-title { font-size: 1.1rem; font-weight: 700; color: var(--text); }
.nav-subtitle { font-size: 0.8rem; color: var(--subtext); }
.btn-list { display: flex; flex-direction: column; gap: 8px; }
.nav-btn {
display: flex; align-items: center; gap: 12px;
width: 100%; padding: 12px 16px;
background-color: var(--card); color: var(--text);
border: 1px solid var(--border); border-radius: 10px;
font-family: 'Inter', sans-serif; font-size: 0.88rem; font-weight: 500;
cursor: pointer; text-align: left;
transition: all 0.2s ease;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
.nav-btn:hover {
background-color: var(--pink); color: #ffffff;
border-color: var(--pink);
transform: translateX(5px);
box-shadow: 0 4px 12px rgba(232, 68, 90, 0.25);
}
.btn-arrow { margin-left: auto; opacity: 0.4; transition: opacity 0.2s; }
.nav-btn:hover .btn-arrow { opacity: 1; }
.empty-state { color: var(--subtext); font-size: 0.85rem; text-align: center; padding: 24px 0; }
Вкладка JS
function render() {
const data = window.DATA.data;
const btnList = document.getElementById('btn-list');
btnList.innerHTML = '';
if (!data || data.length === 0) {
btnList.innerHTML = '<div class="empty-state">Нет доступных дашбордов</div>';
return;
}
data.forEach(row => {
const name = row.dashboard_name?.value || 'Без названия';
// URL берём из agg_value — туда попадает ссылка,
// когда на поле модели включена опция «Является ссылкой»
const url = row.dashboard_url?.agg_value || row.dashboard_url?.value || '#';
// Подбираем иконку по названию дашборда
let icon = '📊';
if (name.toLowerCase().includes('hr') ||
name.toLowerCase().includes('персон')) icon = '👥';
if (name.toLowerCase().includes('продаж')) icon = '📈';
if (name.toLowerCase().includes('склад') ||
name.toLowerCase().includes('поступ')) icon = '📦';
const btn = document.createElement('button');
btn.className = 'nav-btn';
btn.innerHTML =
`<span class="btn-icon">${icon}</span>` +
`<span>${name}</span>` +
`<span class="btn-arrow">→</span>`;
// redirect() — встроенная функция AW BI для навигации из виджета.
btn.addEventListener('click', () => { redirect(url); });
btnList.appendChild(btn);
});
}
Итог
Теперь у нас есть виджет, который открывается по-разному для каждого пользователя, а отображаемые ссылки на дашборды и кнопки соблюдают настройки безопасности в зависимости от отдела сотрудника.
Что мы настроили
- Атрибут
departmentв схеме доступов - Модель
user_permissionsс логинами и атрибутами пользователей - Маппинг провайдера: поле из модели → атрибут схемы
- Модель
dashboard_catalogс каталогом дашбордов - RLS-правило: показывать только строки, где
departmentсовпадает с атрибутом пользователя - HTML-виджет, который динамически рисует кнопки по данным из модели










