Metabase Admin Controller¶
Контроллер: tech.ujin.api.AdminAccessController
Swagger Links¶
- Swagger UI
- OpenAPI YAML
- GET /api/metabase/settings/admin/users
- GET /api/metabase/settings/admin/is-admin
- GET /api/metabase/settings/admin/build-info
- GET /api/metabase/settings/admin/sql/preview
- POST /api/metabase/settings/admin/sql/export
- POST /api/metabase/settings/admin/sql/publish
- POST /api/metabase/settings/admin/sql/deploy
- GET /api/metabase/settings/admin/tags
- POST /api/metabase/settings/admin/tags
- DELETE /api/metabase/settings/admin/tags/{tagId}
- GET /api/metabase/settings/admin/on-prem-boxes
- POST /api/metabase/settings/admin/on-prem-boxes
- DELETE /api/metabase/settings/admin/on-prem-boxes/{id}
- PUT /api/metabase/settings/admin/users/{userId}/tags
- GET /api/metabase/settings/admin/users/{userId}/tags/{tagId}/subfolders
- PUT /api/metabase/settings/admin/users/{userId}/tags/{tagId}/subfolders
- GET /api/metabase/settings/admin/widgets/access
- PATCH /api/metabase/settings/admin/widgets/access
Эндпоинты¶
- GET /api/metabase/settings/admin/users
- Возвращает список LDAP-сотрудников (uid, displayName, givenName, sn, mail, entryUUID)
и назначенные им теги.
- GET /api/metabase/settings/admin/is-admin
- Возвращает true, если текущий LDAP-логин присутствует в app.ldap.adminLogins, иначе false.
- GET /api/metabase/settings/admin/build-info
- Возвращает версию приложения, commit id и build metadata текущей сборки.
- GET /api/metabase/settings/admin/tags
- Возвращает список тегов доступа с type и value.
- POST /api/metabase/settings/admin/tags
- Создает или обновляет тег по code.
- Если type не передан, используется ROOT_COLLECTION.
- DELETE /api/metabase/settings/admin/tags/{tagId}
- Удаляет тег доступа и связанные назначения/матрицу доступов.
- PUT /api/metabase/settings/admin/users/{userId}/tags
- Полностью заменяет набор тегов пользователя (userId = LDAP entryUUID).
- GET /api/metabase/settings/admin/users/{userId}/tags/{tagId}/subfolders
- Возвращает дерево доступных Metabase-подпапок для выбранного пользовательского тега.
- Поле available показывает effective access с учетом наследования deny от родительской папки.
- PUT /api/metabase/settings/admin/users/{userId}/tags/{tagId}/subfolders
- Полностью заменяет deny-list подпапок для выбранного пользовательского тега.
- В хранилище сохраняются только явно запрещенные id подпапок.
- GET /api/metabase/settings/admin/widgets/access?tagId=...
- Возвращает матрицу доступности виджетов/категорий для выбранного тега.
- PATCH /api/metabase/settings/admin/widgets/access
- Включает или отключает доступ к виджету/категории для выбранного тега.
- GET /api/metabase/settings/admin/on-prem-boxes
- Возвращает список on-prem коробок с маппингом domain -> instanceId.
- POST /api/metabase/settings/admin/on-prem-boxes
- Создает или обновляет on-prem коробку по домену.
- DELETE /api/metabase/settings/admin/on-prem-boxes/{id}
- Удаляет on-prem коробку.
- GET /api/metabase/settings/admin/sql/preview?resourceType=...&resourceId=...
- Возвращает formatted preview SQL для question или dashboard.
- POST /api/metabase/settings/admin/sql/export?resourceType=...&resourceId=...
- Выгружает question, dashboard или collection в git-ready файловую структуру.
- POST /api/metabase/settings/admin/sql/publish?resourceType=...&resourceId=...
- Делает export, сравнивает snapshot с GitLab и создает commit + merge request.
- POST /api/metabase/settings/admin/sql/deploy
- Читает snapshot из GitLab и применяет его в текущий Metabase как upsert по entityId.
- При пустом body берется HEAD ветки gitlab.target-branch.
- Поддерживает два варианта доступа: LDAP admin BasicAuth или shared secret header из metabase.deploy.*.
Security¶
- Большинство admin endpoints доступны только через LDAP
BasicAuth. - Для LDAP-сценария логин пользователя должен быть в
app.ldap.adminLogins. - TokenAuth на
/api/metabase/settings/admin/**отклоняется политикойSecurityConfig(403). GET /api/metabase/settings/admin/is-adminтакже требует LDAPBasicAuth; для LDAP-пользователя вне whitelist возвращаетfalse.- Header
X-BMS-DOMENна admin endpoints не используется. - Исключение: git-flow endpoints
POST /api/metabase/settings/admin/sql/export,POST /api/metabase/settings/admin/sql/publishиPOST /api/metabase/settings/admin/sql/deployпринимают либо LDAPBasicAuth, либо deploy token header. - По умолчанию используется header
X-Metabase-Deploy-Token, но имя заголовка может быть переопределено черезmetabase.deploy.token-header-name. - Внутренний prod endpoint
POST /api/internal/metabase/deployне использует LDAP/TokenAuth и защищается shared secret header изmetabase.deploy.*.
Бизнес-логика¶
- Пользователи для
/admin/usersчитаются из LDAP (LdapDirectoryService), теги подтягиваются из сервисной БД. - Теги (
/admin/tags) хранятся вmetabase_access_tag. - On-prem boxes (
/admin/on-prem-boxes) хранятся вmetabase_on_prem_boxкакdomain -> instance_id. - Если домен не найден в
metabase_on_prem_box, token runtime-routing уходит вdefault-source(SaaS). - Runtime cache
domain -> instance_idинвалидируется после create/update/delete on-prem box. - Поддерживаются типы тегов:
ROOT_COLLECTION— назначает корневую коллекцию LDAP-пользователя.FILTER_BMS_ID— добавляет одно значение в список lockedbms_ids.FILTER_INSTANCE_ID— добавляет одно значение в список lockedinstance_ids.- Связь пользователь-тег хранится в
metabase_user_tag(user_id— UUID LDAP пользователя). - Запреты подпапок хранятся в
metabase_user_tag_denied_collection(user_id,tag_id,root_collection_id,collection_id). - Доступ тега к виджетам хранится в
metabase_tag_widget_access(bms_id,tag_id,widget_id). - Для поддерева подпапок используется default-open модель: новые подпапки ничего не требуют в БД и автоматически доступны, пока не появится явный deny.
- При расчете матрицы доступа используется тот же каталог Metabase, что и в settings/widgets flow.
- Scope категорий/виджетов ограничен корневыми коллекциями текущего LDAP-администратора
(
MetabaseCollectionResolver.resolveRootCollectionIds(actor)). - Scope подпапок ограничен root-коллекцией того тега, который уже назначен выбранному пользователю.
- Для одного пользователя допускается любое количество тегов всех трех типов.
- Effective filter-списки для LDAP формируются объединением всех назначенных
FILTER_BMS_IDиFILTER_INSTANCE_ID. - При замене deny-list сервис канонизирует вход: если запрещена родительская подпапка, ее дочерние id отдельно не сохраняются.
- Для git-based аналитики используется отдельный GitLab-проект только с export-файлами.
publishна staging создает MR в этот проект, аdeployна prod читает текущее snapshot-состояние ветки или точного commit SHA и делает snapshot-based sync.- Один и тот же deploy token из
metabase.deploy.*может использоваться как для internal endpoint, так и дляPOST /api/metabase/settings/admin/sql/deploy, если LDAP-admin недоступен. - Collections, questions/cards и dashboards синхронизируются по стабильному
entityId. - Полное archive/delete отсутствующих сущностей пока не включено.
- Dashboard tabs сейчас не создаются автоматически: карточка будет привязана к tab только если на целевом стенде уже есть tab с таким именем.
Git-flow пример переноса коллекции между стендами¶
Пример ниже описывает ручной процесс для коллекции QA. По смыслу этот же flow должен запускаться из UI основного
сервиса кнопкой рядом с действиями вроде избранного или заказа аналитики: если deploy включен и текущий пользователь
является администратором, он может опубликовать в GitLab всю коллекцию, папку, dashboard или отдельный виджет через
POST /api/metabase/settings/admin/sql/publish.
-
На тестовом стенде менеджер готовит изменения в Metabase. Пример исходной коллекции:
https://metabase.unicorn.icu/collection/50-qa. -
Администратор вызывает publish для коллекции
50.
Пример команды:
PUBLISH_URL='https://ujin-api.unicorn.icu/api/metabase/settings/admin/sql/publish'
curl -X 'POST' \
"${PUBLISH_URL}?resourceType=collection&resourceId=50&createArchitectReviewTask=false" \
-H 'accept: application/json' \
-H 'X-Metabase-Deploy-Token: change-me' \
-d ''
Параметры resourceType:
collectionилиfolder— экспорт всей папки/коллекции рекурсивно;dashboard— экспорт dashboard и связанных SQL questions/cards;-
questionилиcard— экспорт отдельного виджета/question. -
Сервис выгружает snapshot, сравнивает его с GitLab и создает merge request. Пример MR:
https://gitlab.unicorn.icu/ujin/metabase-collection/-/merge_requests/6. -
Сревис создает задачу в Jira на Архитектора с указанием МР.
-
Ответственный reviewer проверяет diff и мержит MR в
main. Права на merge вmainнастраиваются в GitLab; список ответственных владельцев процесса описывается отдельно. -
После merge snapshot из
mainприменяется на втором стенде. Сейчас это можно вызвать вручную:
curl -X 'POST' \
'https://ujin-api-git.unicorn.icu/api/metabase/settings/admin/sql/deploy' \
-H 'accept: application/json' \
-H 'X-Metabase-Deploy-Token: change-me' \
-H 'Content-Type: application/json' \
-d '{
"ref": "main"
}'
Целевое состояние процесса: этот шаг должен выполняться автоматически после merge в main,
например GitLab CI/webhook вызывает deploy endpoint второго стенда.
- На втором стенде коллекция появляется или обновляется по стабильным
entityId. Пример результата:https://metabase-git.unicorn.icu/collection/54-qa. При корректном snapshot deploy не должен создавать дубли и мусорные папки.
Валидация¶
codeтега:^[a-z0-9_\\-]+$, max 128.nameтега: max 255.domainon-prem box: должен нормализоваться в непустой hostname.instanceIdon-prem box:- обязателен;
- должен совпадать с одним из настроенных
app.datasource-routing.sources.*; - не должен совпадать с
default-source. value:- обязателен и должен быть положительным числом для
FILTER_BMS_ID; - обязателен и должен быть непустой строкой для
FILTER_INSTANCE_ID; - должен отсутствовать для
ROOT_COLLECTION. tagId,targetId> 0.deniedFolderIds[*]> 0.targetType:WIDGETилиCATEGORY.
Коды ошибок¶
400— невалидные параметры или цель отсутствует в доступном scope.401— нет аутентификации.403— логин не в whitelist или неверный тип auth.500— внутренняя ошибка.