Ролевая модель¶
Модуль CM Ocean Loyalty работает как OAuth2 resource server: бэкенд loyalty-billing
проверяет JWT, выпущенный Keycloak, не хранит сессий (stateless) и придерживается принципа
deny-by-default — любой вызов API требует аутентификации, а на каждой операции навешана
проверка прав @PreAuthorize. Доступ к функции есть только тогда, когда у пользователя
(или сервисного клиента) явно присутствует соответствующая роль.
Откуда берутся роли¶
Роли приходят в JWT в клейме resource_access[<client>].roles и имеют продуктовый префикс
LOYALTY_. Имя роли построено по шаблону LOYALTY_<ДОМЕН>_<ДЕЙСТВИЕ>, где действие — это
READ (чтение) либо WRITE (изменение); для части функций действие разбито тоньше: право на
справочник тегов и отдельное право на присвоение тега клиенту, право правки акции и отдельные права
на её согласование (APPROVE) и запуск (ACTIVATE), право правки конфигурации программы и отдельное
право на её активацию, отдельные права на активацию и удаление версий настройки карточки клиента
(ACTIVATE, DELETE), отдельное право на приём сегментов (INTAKE). Назначение и выдача ролей
выполняются на стороне Keycloak — client-роли на общем клиенте входа frontend (том же, что у Core UI
и модуля Promo); модуль их только проверяет.
Типы клиентов
Интерактивный (UI) клиент использует authorization code flow — через него работают операторы из единого окна Core UI. Сервисные и интеграционные потребители (POS, RT Flow, Check Scan, модуль Promo, Batch Flow и т. п.) используют client credentials. Набор проверяемых ролей для обоих типов клиентов одинаков — различается лишь способ получения токена.
Клиентский кабинет (B2C) — отдельный контур
Мобильный кабинет участника (/v1/b2c/**) защищён не ролями LOYALTY_*, а отдельным
scope loyalty.b2c клиентского токена (см. «B2C-кабинет»). Единственное
пересечение с ролевой моделью — staff-предпросмотр: сотрудник с ролью LOYALTY_PROFILE_READ
может открыть кабинет без клиентского токена, для поддержки. Участник выбирается параметром
запроса previewExternalId; без параметра открывается участник, заданный настройкой
loyalty.b2c.preview-external-id (см. Конфигурация). Просмотр с явно
переданным previewExternalId попадает в журнал аудита как обращение к персональным данным
участника. Клиентская ветка параметр не читает.
Реестр сотрудников
Бэкенд запоминает сотрудников по их токенам — идентификатор, имя, логин, e-mail и роли
LOYALTY_* — без обращения к Keycloak Admin API. В реестр попадают только люди с ролями продукта,
входившие хотя бы раз; клиентские токены и сервисные учётки интеграций (service-account-*) в
него не попадают. Реестр — источник списка согласующих при отправке акции на согласование
(GET /v1/users) и подписей «ФИО (логин)» у автора и активировавшего конфигурацию программы;
e-mail наружу не отдаётся. Пока сотрудник
ни разу не вошёл после выдачи роли, в списках его нет.
Публичные эндпоинты¶
Без аутентификации доступны только следующие эндпоинты:
| Назначение | Путь | Порт |
|---|---|---|
| Проверки здоровья (k8s liveness/readiness) | GET /health/** |
management, 9090 |
| Метрики Prometheus | /metrics |
management, 9090 |
| Информация о сборке | /info |
management, 9090 |
| Спецификация OpenAPI / Swagger UI | /v3/api-docs/**, /swagger-ui/** |
прикладной, 8080 |
Вся бизнес-функциональность API (/v1/...) закрыта и требует роли из таблиц ниже. Исключение —
часовой пояс программы GET /v1/program/timezone: он доступен любому аутентифицированному
сотруднику без роли продукта, потому что по нему интерфейс показывает все даты.
Полный перечень ролей¶
Ниже — весь состав ролей LOYALTY_*, объявленных в реестре ролей бэкенда и заведённых как
клиентские роли на клиенте frontend в Keycloak, сгруппированный по предметной области. Столбец
«Кому выдаётся» — ориентир, а не жёсткое предписание: конкретный набор ролей у пользователя
определяет администратор Keycloak.
Клиенты, профиль, теги¶
| Роль | Что открывает | Кому выдаётся |
|---|---|---|
LOYALTY_CUSTOMER_READ |
Каталог клиентов, карточка клиента, схема и структура карточки клиента, справочники полей профиля, строки таблиц данных по клиенту | Оператор |
LOYALTY_CUSTOMER_WRITE |
Создание клиента, правка профиля (запись в MDM), блокировка/разблокировка, архивирование/разархивирование, мягкое удаление, слияние «1 телефон = 1 профиль», массовые операции каталога, импорт клиентов, обезличивание (наравне с LOYALTY_INTEGRATION_WRITE) |
Оператор |
LOYALTY_PROFILE_READ |
Сводный профиль клиента (идентификация MDM + данные лояльности), бейджи клиента; также даёт staff-доступ к B2C-кабинету участника для поддержки | Оператор |
LOYALTY_ACCOUNT_READ |
Счета клиента и баланс баллов (доступно / в резерве) | Оператор |
LOYALTY_ACCOUNT_WRITE |
Создание счёта клиента | Оператор |
LOYALTY_TAG_READ |
Справочник тегов клиента — просмотр, выбор тегов в фильтре каталога и в условиях аудитории акций | Оператор, маркетолог |
LOYALTY_TAG_WRITE |
Справочник тегов — создание, переименование, слияние, удаление, перенос; заведение недостающих тегов при создании клиента по API и импортом | Администратор НСИ |
LOYALTY_CUSTOMER_TAG_WRITE |
Присвоение и снятие тегов клиенту (отдельно от справочника и от LOYALTY_CUSTOMER_WRITE) — в каталоге клиентов и операциями «Добавить теги» / «Снять теги» экрана «Ручные операции» |
Оператор |
LOYALTY_CLIENT_PROFILE_ACTIVATE |
Активация версии настройки карточки клиента. Правка черновика настройки остаётся под LOYALTY_DICTIONARY_WRITE и права на активацию не даёт |
Администратор НСИ, рецензент настройки |
LOYALTY_CLIENT_PROFILE_DELETE |
Удаление версий настройки карточки клиента (одиночное и пакетное). Ни правка, ни активация этого права не дают: удалённую версию не вернуть | Администратор НСИ |
Операции с баллами¶
| Роль | Что открывает | Кому выдаётся |
|---|---|---|
LOYALTY_ACCRUAL_WRITE |
Начисление баллов — одиночное, массовое и через экран «Ручные операции» (пакет по списку, файлом) | Оператор ручных операций |
LOYALTY_REDEMPTION_WRITE |
Списание: резерв (hold), фиксация/отмена резерва, прямое списание, корректировка, ручное сгорание (/expirations) |
Оператор ручных операций |
LOYALTY_TIER_WRITE (в этой роли) |
Перевод уровня клиента как ручная операция экрана «Ручные операции» | Оператор ручных операций |
LOYALTY_CUSTOMER_TAG_WRITE (в этой роли) |
Добавление и снятие тегов по списку клиентов или файлу на экране «Ручные операции» | Оператор ручных операций |
LOYALTY_OPERATION_REASON_OTHER_WRITE |
Причина «Прочее» справочника причин операций — поверх права на саму операцию (ACCRUAL_WRITE/REDEMPTION_WRITE/TIER_WRITE) |
Оператор с расширенным доступом (не выдаётся всем операторам ручных операций) |
LOYALTY_OPERATION_WRITE |
Пополнение, списание, перевод и обмен баллов между счетами леджера (/top-up, /withdraw, /transfer, /exchange) |
Оператор финансовых операций |
LOYALTY_EVENT_WRITE |
Регистрация покупки (покупка → акция → начисление) | Интеграция (касса/POS) |
LOYALTY_MAINTENANCE_WRITE |
Регламентное сгорание просроченных баллов, постановка предупреждений о сгорании в очередь, запуск внутренней сверки итогов (/admin/reconciliation/runs) |
Эксплуатация |
Уровни и конфигурация программы¶
| Роль | Что открывает | Кому выдаётся |
|---|---|---|
LOYALTY_TIER_READ |
Уровни (по умолчанию — активной конфигурации программы), параметры уровня (кастомные поля), текущий уровень клиента с прогрессом, журнал переходов | Оператор, маркетолог |
LOYALTY_TIER_WRITE |
Параметры уровня — поля и их значения, ручное назначение/снятие уровня клиента (override + причина), массовый перевод уровня. Сам набор уровней правится только в составе конфигурации программы | Администратор программы |
LOYALTY_PROGRAM_CONFIG_READ |
Раздел «Конфигурации программы»: каталог, карточка конфигурации, история активаций, список авторов, базовая денежная валюта для мастера | Администратор программы, маркетолог |
LOYALTY_PROGRAM_CONFIG_WRITE |
Создание конфигурации, правка черновика, дублирование, удаление (одиночное и пакетное) | Администратор программы |
LOYALTY_PROGRAM_CONFIG_ACTIVATE |
Активация конфигурации программы. Право правки его не подразумевает — роли отзываются независимо | Администратор программы |
Акции, согласование, метки, достижения¶
| Роль | Что открывает | Кому выдаётся |
|---|---|---|
LOYALTY_PROMOTION_READ |
Каталог механик, список и карточки акций, статистика, история согласования, реестр условий и справочники формы акции, партнёры и партнёрские предложения (чтение), экспорт акций/механик между контурами | Маркетолог |
LOYALTY_PROMOTION_WRITE |
Создание, изменение, приостановка, отмена запуска, архивирование/возврат из архива, удаление, копия, новая версия правила, массовые действия, отправка на согласование и её отмена, выбор согласующих из реестра сотрудников, партнёры и партнёрские предложения (запись), проставление и снятие меток на акции, импорт акций/механик. Запуск акции не открывает | Маркетолог |
LOYALTY_PROMOTION_ACTIVATE |
Запуск акции — одиночный и массовый; требуется всегда, независимо от того, включено ли согласование | Маркетолог или ответственный за запуск |
LOYALTY_PROMOTION_APPROVE |
Согласование и отклонение запуска акции (принцип «четырёх глаз»); решение по своей же отправке отклоняется | Согласующий — не автор акции |
LOYALTY_PROMOTION_LABEL_READ |
Справочник меток акций — просмотр, выбор меток в форме акции и в фильтре каталога | Маркетолог |
LOYALTY_PROMOTION_LABEL_WRITE |
Справочник меток — создание, переименование, слияние, удаление, перенос; заведение недостающих меток при создании акции по API | Маркетолог (по умолчанию — обе роли на метки) |
LOYALTY_ACHIEVEMENT_READ |
Каталог правил достижений «условие — бейдж» — просмотр | Маркетолог |
LOYALTY_ACHIEVEMENT_WRITE |
Каталог достижений — создание, изменение, удаление; пересчёт бейджей клиента | Маркетолог |
Согласование запуска акции
Согласование включается настройкой развёртывания loyalty.promotion.approval.enabled (env
LOYALTY_PROMOTION_APPROVAL_ENABLED, по умолчанию выключено). При выключенном согласовании его
действия отвечают 409 approval_disabled, а запуск идёт без согласования — но всё равно только
с правом LOYALTY_PROMOTION_ACTIVATE. При включённом запуск несогласованного черновика
отклоняется 409 approval_required.
Сегменты клиентов¶
| Роль | Что открывает | Кому выдаётся |
|---|---|---|
LOYALTY_SEGMENT_READ |
Экран «Сегменты клиентов»: список сегментов с числом клиентов и действующих акций, журнал заливок | Маркетолог, администратор НСИ |
LOYALTY_SEGMENT_WRITE |
Архивация, возврат из архива и удаление сегмента | Администратор НСИ |
LOYALTY_SEGMENT_INTAKE |
Приём сегментов из Batch Flow (/v1/integration/customer-segments/**): регистрация сегмента, пачки участников, завершение заливки |
Только учётная запись Batch Flow |
Приём — отдельное право, а не общее интеграционное: заливка меняет, кому начисляют все сегментные акции сразу.
Валюты и курсы¶
| Роль | Что открывает | Кому выдаётся |
|---|---|---|
LOYALTY_CURRENCY_READ |
Просмотр справочника валют (GET /v1/currencies) |
Оператор, маркетолог |
LOYALTY_EXCHANGE_RATE_READ |
Просмотр курсов обмена между валютами баллов | Оператор, маркетолог |
Ведение справочника валют программы (создание, правка, история курса) и пар обмена валют идёт под
LOYALTY_DICTIONARY_READ/LOYALTY_DICTIONARY_WRITE — как и у прочих разделов администрирования, а
не под LOYALTY_CURRENCY_*.
Справочники (НСИ) и настройка программы¶
| Роль | Что открывает | Кому выдаётся |
|---|---|---|
LOYALTY_DICTIONARY_READ |
Реестр справочников и записи справочников общего реестра, справочник валют программы и пары обмена, субсчета баллов (наравне с правом на ручные операции), операционная конфигурация, лимиты и политика списания, глобальная механика, настройка профиля клиента (поля, группы, таблицы, версии, источники данных), реестр условий акции («Настройка акции»), список городов профиля без определённого часового пояса | Администратор НСИ, оператор ручных операций (нужна для чтения справочника причин операций — см. предупреждение ниже) |
LOYALTY_DICTIONARY_WRITE |
Запись перечисленного выше: создание/изменение/удаление записей локальных справочников, субсчета, справочник валют и пары обмена, операционная конфигурация, лимиты и политика списания, глобальная механика, правка настройки профиля клиента (без активации и удаления версий), реестр условий акции | Администратор НСИ |
LOYALTY_CITY_READ / LOYALTY_CITY_WRITE |
Справочник городов (/v1/admin/cities): просмотр; создание, правка, архивация |
Администратор НСИ |
LOYALTY_STORE_READ / LOYALTY_STORE_WRITE |
Справочник торговых точек (/v1/admin/stores): просмотр; создание, правка, закрытие |
Администратор НСИ |
LOYALTY_MERCHANT_READ / LOYALTY_MERCHANT_WRITE |
Справочник мерчантов (/v1/admin/merchants): просмотр; создание, правка, закрытие |
Администратор НСИ |
LOYALTY_PRODUCT_CATALOG_READ / LOYALTY_PRODUCT_CATALOG_WRITE |
Каталог товаров — категории (/v1/admin/product-categories) и товары (/v1/admin/product-items): просмотр; создание, правка, архивация, удаление, импорт из CSV |
Администратор НСИ |
Справочники со своим экраном — исключение
Общие LOYALTY_DICTIONARY_READ/LOYALTY_DICTIONARY_WRITE не открывают справочники со
своим экраном: теги клиента, метки акций, сегменты клиентов, города, торговые точки, мерчанты,
категории товаров и SKU. У каждого своя пара ролей (см. таблицы выше; категории и SKU делят
LOYALTY_PRODUCT_CATALOG_*). Строка такого справочника в общем реестре
(GET /v1/admin/dictionaries) видна только с его собственной read-ролью; вызов
generic-эндпоинтов по его ключу отвечает 409 dictionary_own_screen. Сам реестр читается только
с LOYALTY_DICTIONARY_READ: без неё экраны «Теги клиентов», «Метки акций» и «Сегменты клиентов»
открываются лишь по прямой ссылке. Подробнее — «Справочники и НСИ».
LOYALTY_DICTIONARY_READ нужна операторам операций с баллами и уровнем
Причины начисления, корректировки и перевода уровня — коды справочника «Причины операций»,
который формы всех трёх операций читают через GET /v1/admin/dictionaries/{key}/entries. Без
LOYALTY_DICTIONARY_READ форма операции сообщает «Справочник причин недоступен — операция
невозможна», даже если у оператора есть право на саму операцию. Операциям с тегами причина не
нужна.
Мониторинг, бизнес-день, аудит, отчёты¶
| Роль | Что открывает | Кому выдаётся |
|---|---|---|
LOYALTY_MONITORING_READ |
Сводки и динамика, аномалии операций, баллы к сгоранию, журнал ручных начислений, история операций клиента, текущий бизнес-день и дневные отчёты, раздел «Отчёты» (/v1/reports/**), сквозной журнал аудита (список, целостность, выгрузка), список и диффы прогонов сверки итогов |
Мониторинг, аналитик, аудитор, эксплуатация |
LOYALTY_BUSINESS_DAY_WRITE |
Закрытие текущего бизнес-дня | Эксплуатация |
Собственной read-роли у журнала аудита, у сверки итогов и у отчётов нет — все три читаются той же
ролью LOYALTY_MONITORING_READ, что и остальной мониторинг. Вкладки раздела «Отчёты» между собой по
ролям не разграничены.
Очередь приёма¶
| Роль | Что открывает | Кому выдаётся |
|---|---|---|
LOYALTY_INTAKE_READ |
Экран «Очередь приёма»: список и карточка отложенной команды | Администратор, эксплуатация |
LOYALTY_INTAKE_WRITE |
Подача команды в очередь (POST /v1/intake/commands — интеграционная точка входа, не экран); на экране очереди — «Вернуть в работу» и «Отменить» (в т. ч. отменить команду, назначенную на будущее) |
Интеграция (подача команд), администратор/эксплуатация (разбор очереди) |
Исходящие уведомления¶
| Роль | Что открывает | Кому выдаётся |
|---|---|---|
LOYALTY_NOTIFICATION_READ |
Сообщения очереди исходящих уведомлений, отложенные в разбор (GET /v1/admin/notifications) |
Эксплуатация |
LOYALTY_NOTIFICATION_WRITE |
Возврат сообщения в очередь и снятие из разбора с основанием | Эксплуатация |
Экрана у разбора исходящих уведомлений нет — он выполняется через API.
Обмен данными¶
| Роль | Что открывает | Кому выдаётся |
|---|---|---|
LOYALTY_EXCHANGE_READ |
Ленты изменений (клиенты, операции, счета) и журнал прогонов регламентного обмена с внешними системами и DWH | Интеграция, DWH |
LOYALTY_EXCHANGE_WRITE |
Управление регламентом обмена: расписание задания, ручной запуск, изменение порогов | Интеграция, эксплуатация |
LOYALTY_EXCHANGE_READ намеренно отделена от LOYALTY_INTEGRATION_READ: лента изменений
отдаёт реестр клиентов целиком, включая персональные данные, а не точечную резолюцию по
одному клиенту — поэтому её не раздают тем же клиентам, что несут LOYALTY_INTEGRATION_*.
Интеграции¶
| Роль | Что открывает | Кому выдаётся |
|---|---|---|
LOYALTY_INTEGRATION_READ |
Резолюция карты лояльности на POS, обзор клиента для Check Scan, сегменты клиента, проверка условий для RT Flow (read/resolve-контур, без записи) | Интеграция (POS, Check Scan, RT Flow) |
LOYALTY_INTEGRATION_WRITE |
Приём чеков POS и возвратов по чеку (с отменой возврата), начислений и сторно от модуля Promo, начислений от RT Flow, транзакций партнёрской программы, внешних итогов для сверки; наравне с LOYALTY_CUSTOMER_WRITE допускается к обезличиванию клиента |
Интеграция (POS, Promo, RT Flow, партнёрские системы) |
Составные правила доступа¶
Часть операций требует не одной роли, а их сочетания или альтернативы — правило собрано в
реестре ролей и проверяется либо на самой ручке (@PreAuthorize), либо в сервисе, когда
решение зависит от данных запроса:
| Правило | Условие | Где применяется |
|---|---|---|
| Карточка клиента | LOYALTY_PROFILE_READ И LOYALTY_MONITORING_READ |
GET /v1/customers/{externalId}/card, реферальный блок и список рефералов, партии баллов (points-lots), сегменты клиента, история статусов |
| Ручная операция (любая) | LOYALTY_ACCRUAL_WRITE ИЛИ LOYALTY_REDEMPTION_WRITE ИЛИ LOYALTY_TIER_WRITE ИЛИ LOYALTY_CUSTOMER_TAG_WRITE |
Открытие экрана «Ручные операции», приём и статус пакета, отчёт по пакету, импорт файла; право на конкретную выбранную операцию в пакете проверяется по ней (начисление — ACCRUAL_WRITE, корректировка — REDEMPTION_WRITE, перевод уровня — TIER_WRITE, добавление и снятие тегов — CUSTOMER_TAG_WRITE), а не по факту доступа к ручке |
| Просмотр субсчетов | LOYALTY_DICTIONARY_READ ИЛИ любая из ролей ручной операции |
GET /v1/points-subaccounts (список нужен и при выборе субсчёта в форме операции) |
| Список тегов | LOYALTY_TAG_READ ИЛИ LOYALTY_CUSTOMER_TAG_WRITE |
GET /v1/tags, POST /v1/tags/lookup — тому, кто только присваивает теги, тоже нужно из чего выбирать |
| Список сегментов | LOYALTY_SEGMENT_READ ИЛИ LOYALTY_PROMOTION_READ ИЛИ LOYALTY_DICTIONARY_READ ИЛИ LOYALTY_CUSTOMER_READ |
GET /v1/customer-segments — поле выбора сегмента в акциях, партнёрских предложениях, ограничениях списания и каталоге клиентов |
| Создание клиента с тегами | LOYALTY_CUSTOMER_WRITE, а если запрос несёт tags/tagIds — дополнительно LOYALTY_CUSTOMER_TAG_WRITE |
POST /v1/customers |
| Обезличивание ПДн | LOYALTY_CUSTOMER_WRITE ИЛИ LOYALTY_INTEGRATION_WRITE |
POST /v1/customers/{externalId}/anonymize |
| Действие согласования акции | На ручке — LOYALTY_PROMOTION_WRITE ИЛИ LOYALTY_PROMOTION_APPROVE; сервис уточняет: submit/cancel — право записи (иначе 403 access_denied), approve/reject — право согласующего (иначе 403 approval_forbidden) |
POST /v1/promotions/{id}/approvals/{type}; решение по своей же отправке — 409 approval_self_forbidden, при поимённом списке решают только перечисленные — иначе 403 approval_not_assigned |
| Сохранение конфигурации с акциями уровней | LOYALTY_PROGRAM_CONFIG_WRITE, а если сохранение меняет условия акций (множество акций уровня, снятие с программы акции, стоящей на уровне, удаление уровня с акциями) — дополнительно LOYALTY_PROMOTION_WRITE |
POST /v1/program-configs, PATCH /v1/program-configs/{id}; дублирование и удаление конфигурации этого права не требуют |
Заведение недостающего значения справочника из данных
Создание/импорт клиента с незнакомым тегом заводит его в справочнике только при наличии
LOYALTY_TAG_WRITE у вызывающего — иначе 422 unknown_tag. Симметрично для акций:
незнакомая метка заводится только при LOYALTY_PROMOTION_LABEL_WRITE, иначе
422 unknown_label. Это не @PreAuthorize, а проверка внутри сервиса по фактическим
authorities текущего субъекта.
Соответствие ролей и операций¶
Привязка «роль → метод и путь» приведена ниже. Префикс API — /v1.
Клиенты, счета, баланс, профиль, теги¶
| Роль | Метод и путь |
|---|---|
LOYALTY_CUSTOMER_READ |
GET /v1/customers, GET /v1/customers/{externalId}, GET /v1/client-profile/schema, GET /v1/client-profile/structure, GET /v1/client-profile/dictionaries/{key}, GET /v1/customers/{externalId}/profile-tables/{groupId}/rows |
LOYALTY_CUSTOMER_WRITE |
PUT /v1/customers/{externalId}/profile, POST /v1/customers/{externalId}/block\|unblock\|merge\|archive\|unarchive, DELETE /v1/customers/{externalId}, PUT /v1/customers/{externalId}/attributes, POST /v1/customers/bulk-block\|bulk-unblock\|bulk-archive\|bulk-unarchive\|bulk-delete, POST /v1/customers/import, POST /v1/customers/import/validate |
| Создание клиента с тегами¹ | POST /v1/customers |
| Обезличивание ПДн¹ | POST /v1/customers/{externalId}/anonymize |
LOYALTY_ACCOUNT_READ |
GET /v1/customers/{externalId}/accounts, GET /v1/customers/{externalId}/accounts/{accountNumber}, GET /v1/customers/{externalId}/balance |
LOYALTY_ACCOUNT_WRITE |
POST /v1/customers/{externalId}/accounts |
LOYALTY_PROFILE_READ |
GET /v1/customers/{externalId}/profile, GET /v1/customers/{externalId}/achievements |
| Карточка клиента¹ | GET /v1/customers/{externalId}/card, GET /v1/customers/{externalId}/referral, GET /v1/customers/{externalId}/referrals, GET /v1/customers/{externalId}/points-lots, GET /v1/customers/{externalId}/segments, GET /v1/customers/{externalId}/status-history |
LOYALTY_DICTIONARY_READ |
GET /v1/admin/client-profile/fields, GET /v1/admin/client-profile/groups, GET /v1/admin/client-profile/groups/{id}/columns, GET /v1/admin/client-profile/table-sections, GET /v1/admin/client-profile/source-schemas\|source-tables\|source-columns, GET /v1/admin/client-profile/configurations, GET /v1/admin/client-profile/configurations/{id}, GET .../configurations/{id}/diff |
LOYALTY_DICTIONARY_WRITE |
POST/PUT/DELETE /v1/admin/client-profile/fields* (в том числе order, bulk-delete, {id}/archive, {id}/unarchive), POST/PUT/DELETE /v1/admin/client-profile/groups* (в том числе order, {id}/columns), POST/PUT/DELETE /v1/admin/client-profile/table-sections* (в том числе order), PATCH /v1/admin/client-profile/configurations/{id}, POST .../configurations/{id}/copy |
LOYALTY_CLIENT_PROFILE_ACTIVATE |
POST /v1/admin/client-profile/configurations/{id}/activate |
LOYALTY_CLIENT_PROFILE_DELETE |
DELETE /v1/admin/client-profile/configurations/{id}, POST /v1/admin/client-profile/configurations/bulk-delete |
LOYALTY_TAG_READ или LOYALTY_CUSTOMER_TAG_WRITE |
GET /v1/tags, POST /v1/tags/lookup |
LOYALTY_TAG_READ |
GET /v1/tags/customer-counts, GET /v1/tags/{tagId}/condition-promotions, GET /v1/tags/condition-promotions |
LOYALTY_TAG_WRITE |
POST /v1/tags, PATCH /v1/tags/{tagId}, POST /v1/tags/{tagId}/merge, POST /v1/tags/bulk-delete, GET /v1/tags/export, POST /v1/tags/import |
LOYALTY_CUSTOMER_TAG_WRITE |
POST /v1/customers/bulk-tag-assign, POST /v1/customers/bulk-tag-remove |
¹ Составное правило — см. раздел «Составные правила доступа» выше; это не имя роли в Keycloak.
Начисления, списания, ручные операции¶
| Роль | Метод и путь |
|---|---|
LOYALTY_ACCRUAL_WRITE |
POST /v1/customers/{externalId}/accruals, POST /v1/customers/bulk-accruals |
LOYALTY_REDEMPTION_WRITE |
POST /v1/customers/{externalId}/holds, POST /v1/customers/{externalId}/holds/{holdId}/commit\|cancel, POST /v1/customers/{externalId}/redemptions, POST /v1/customers/{externalId}/corrections, POST /v1/customers/{externalId}/expirations, POST /v1/customers/bulk-corrections |
LOYALTY_TIER_WRITE |
POST /v1/customers/tier-bulk (в составе ручных операций) |
LOYALTY_OPERATION_REASON_OTHER_WRITE |
Причина OTHER в теле любой из ручных операций выше — проверяется сервисом, не отдельным путём |
| Право на ручную операцию (любую)¹ | POST /v1/customers/operations/batches (приём пакета, в том числе операций с тегами), GET /v1/customers/operations/batches, GET .../batches/{batchId}, GET .../batches/{batchId}/events, GET .../batches/{batchId}/report, POST /v1/customers/operations/import/validate, POST /v1/customers/operations/import/validate/report |
LOYALTY_OPERATION_WRITE |
POST /v1/top-up, POST /v1/withdraw, POST /v1/transfer, POST /v1/exchange |
LOYALTY_EVENT_WRITE |
POST /v1/events/purchase |
LOYALTY_MAINTENANCE_WRITE |
POST /v1/maintenance/points/expire, POST /v1/maintenance/points/expiry-notice, POST /v1/admin/reconciliation/runs |
Уровни и конфигурация программы¶
| Роль | Метод и путь |
|---|---|
LOYALTY_TIER_READ |
GET /v1/tiers (необязательный configId), GET /v1/customers/{externalId}/tier, GET /v1/customers/{externalId}/tier/transitions, GET /v1/tiers/{code}/attributes, GET /v1/tier-profile/schema, GET /v1/admin/tier-profile/fields |
LOYALTY_TIER_WRITE |
PUT /v1/customers/{externalId}/tier, DELETE /v1/customers/{externalId}/tier, PUT /v1/tiers/{code}/attributes, POST/PUT/DELETE /v1/admin/tier-profile/fields* (в том числе order) |
LOYALTY_PROGRAM_CONFIG_READ |
GET /v1/program-configs, GET /v1/program-configs/{id}, GET /v1/program-configs/activations, GET /v1/program-configs/activations/{activationId}, GET /v1/program-configs/authors, GET /v1/program-configs/base-currency |
LOYALTY_PROGRAM_CONFIG_WRITE |
POST /v1/program-configs, PATCH /v1/program-configs/{id}, DELETE /v1/program-configs/{id}, POST /v1/program-configs/bulk-delete, POST /v1/program-configs/duplicate |
LOYALTY_PROGRAM_CONFIG_ACTIVATE |
POST /v1/program-configs/{id}/activate |
Правка и удаление уровня отдельным путём (PUT/DELETE /v1/tiers/{code}) не поддерживаются:
набор уровней сохраняется только в составе конфигурации программы.
Акции, согласование, метки, достижения, партнёры¶
| Роль | Метод и путь |
|---|---|
LOYALTY_PROMOTION_READ |
GET /v1/mechanics*, GET /v1/promotions* (в том числе stats, authors, criteria, {id}/stats, {id}/approvals, {id}/criteria/attribute-activity, .../participation-limit), GET /v1/promotions/dictionaries/{key}/entries, POST /v1/promotions/dictionaries/{key}/entries/lookup, GET /v1/admin/promotions/export, GET /v1/admin/mechanics/export, GET /v1/partners*, GET /v1/partners/{id}/settlement, GET /v1/partner-offers* |
LOYALTY_PROMOTION_WRITE |
POST/PUT/DELETE /v1/promotions*, действия ЖЦ (unschedule, pause, archive, unarchive, revise, copy) и их bulk-варианты, POST /v1/promotions/bulk-delete, PUT/DELETE .../participation-limit, POST /v1/promotions/bulk-label-assign\|bulk-label-remove, POST /v1/admin/promotions/import, POST /v1/admin/mechanics/import, POST/PUT/DELETE /v1/partners*, POST/PUT/DELETE /v1/partner-offers*, POST /v1/partner-offers/{id}/status, GET /v1/users |
LOYALTY_PROMOTION_ACTIVATE |
POST /v1/promotions/{id}/activate, POST /v1/promotions/bulk-activate |
| Действие согласования¹ | POST /v1/promotions/{id}/approvals/{submit\|approve\|reject\|cancel} |
LOYALTY_PROMOTION_LABEL_READ |
GET /v1/promotion-labels, POST /v1/promotion-labels/lookup, GET .../condition-promotions |
LOYALTY_PROMOTION_LABEL_WRITE |
POST /v1/promotion-labels, PATCH /v1/promotion-labels/{id}, POST /v1/promotion-labels/{id}/merge, POST /v1/promotion-labels/bulk-delete, GET /v1/promotion-labels/export, POST /v1/promotion-labels/import |
LOYALTY_ACHIEVEMENT_READ |
GET /v1/admin/achievements, GET /v1/admin/achievements/{id} |
LOYALTY_ACHIEVEMENT_WRITE |
POST/PUT/DELETE /v1/admin/achievements*, POST /v1/customers/{externalId}/achievements/recheck |
Сегменты клиентов¶
| Роль | Метод и путь |
|---|---|
| Список сегментов¹ | GET /v1/customer-segments |
LOYALTY_SEGMENT_READ |
GET /v1/admin/customer-segments/fills, GET /v1/admin/customer-segments/fills/segments |
LOYALTY_SEGMENT_WRITE |
POST /v1/customer-segments/{code}/archive\|unarchive, DELETE /v1/customer-segments/{code} |
LOYALTY_SEGMENT_INTAKE |
PUT /v1/integration/customer-segments/{code}, POST .../{code}/fills/{fillId}/members, POST .../{code}/fills/{fillId}/complete |
Валюты, курсы, справочники, конфигурация¶
| Роль | Метод и путь |
|---|---|
LOYALTY_CURRENCY_READ |
GET /v1/currencies, GET /v1/currencies/{code} |
LOYALTY_EXCHANGE_RATE_READ |
GET /v1/exchange-rates |
LOYALTY_DICTIONARY_READ |
GET /v1/admin/dictionaries*, GET /v1/admin/dictionaries/export, GET /v1/admin/currencies* (в том числе {code}/rates), GET /v1/admin/exchange-pairs* (в том числе summary, base-rate), GET /v1/admin/point-currencies, GET /v1/admin/system-config, GET /v1/admin/redemption-limits, GET /v1/admin/redemption-policy, GET /v1/admin/global-mechanic, GET /v1/admin/promotion-criteria, GET /v1/admin/promotion-criteria/attribute-stats, GET /v1/admin/timezones/unmatched-cities, GET /v1/points-subaccounts/{id}, GET /v1/points-subaccounts/{id}/totals |
LOYALTY_DICTIONARY_WRITE |
POST/DELETE/status /v1/admin/dictionaries/{key}/entries*, POST /v1/admin/dictionaries/import, POST/PUT/DELETE /v1/admin/currencies*, POST/PUT/DELETE /v1/admin/exchange-pairs*, PUT/DELETE /v1/admin/point-currencies/{code}, PUT /v1/admin/system-config/{key}, PUT/DELETE /v1/admin/redemption-limits/{scopeType}/{scopeKey}, PUT /v1/admin/redemption-policy, PUT /v1/admin/global-mechanic, PUT /v1/admin/promotion-criteria, POST /v1/points-subaccounts, PUT /v1/points-subaccounts/{id}, PUT /v1/points-subaccounts/priority, POST /v1/points-subaccounts/{id}/activate\|close |
LOYALTY_DICTIONARY_READ или право на ручную операцию¹ |
GET /v1/points-subaccounts (список без {id}) |
LOYALTY_CITY_READ / LOYALTY_CITY_WRITE |
GET /v1/admin/cities, GET /v1/admin/cities/{code} / POST /v1/admin/cities, PUT /v1/admin/cities/{code}, POST /v1/admin/cities/{code}/status |
LOYALTY_STORE_READ / LOYALTY_STORE_WRITE |
GET /v1/admin/stores, GET /v1/admin/stores/{code} / POST /v1/admin/stores, PUT /v1/admin/stores/{code}, POST /v1/admin/stores/{code}/status |
LOYALTY_MERCHANT_READ / LOYALTY_MERCHANT_WRITE |
GET /v1/admin/merchants, GET /v1/admin/merchants/{code} / POST /v1/admin/merchants, PUT /v1/admin/merchants/{code}, POST /v1/admin/merchants/{code}/status |
LOYALTY_PRODUCT_CATALOG_READ |
GET /v1/admin/product-categories, GET /v1/admin/product-categories/{code}, GET /v1/admin/product-items, GET /v1/admin/product-items/{code} |
LOYALTY_PRODUCT_CATALOG_WRITE |
POST /v1/admin/product-categories, PUT/DELETE /v1/admin/product-categories/{code}, POST .../{code}/status, POST /v1/admin/product-categories/import, POST .../import/validate; то же для /v1/admin/product-items |
Мониторинг, бизнес-день, аудит, отчёты, сверка¶
| Роль | Метод и путь |
|---|---|
LOYALTY_MONITORING_READ |
GET /v1/monitoring/summary\|anomalies\|expiring\|manual-accruals, GET /v1/customers/{externalId}/operations, GET /v1/business-day, GET /v1/business-day/{date}/summary, GET /v1/reports/pulse\|points\|tiers\|referrals, GET /v1/reports/promotions, GET /v1/reports/promotions/totals, GET /v1/reports/promotions/export, GET /v1/reports/promotions/{rootId}/dynamics, GET /v1/admin/audit, GET /v1/admin/audit/integrity, GET /v1/admin/audit/export, GET /v1/admin/reconciliation/runs, GET /v1/admin/reconciliation/runs/{runId}/diffs |
LOYALTY_BUSINESS_DAY_WRITE |
POST /v1/business-day/close |
Очередь приёма и исходящие уведомления¶
| Роль | Метод и путь |
|---|---|
LOYALTY_INTAKE_WRITE |
POST /v1/intake/commands (подача), POST /v1/admin/intake/commands/{eventId}/retry\|cancel\|discard |
LOYALTY_INTAKE_READ |
GET /v1/admin/intake/commands, GET /v1/admin/intake/commands/{eventId} |
LOYALTY_NOTIFICATION_READ |
GET /v1/admin/notifications |
LOYALTY_NOTIFICATION_WRITE |
POST /v1/admin/notifications/{id}/retry, POST /v1/admin/notifications/{id}/discard |
Обмен данными¶
| Роль | Метод и путь |
|---|---|
LOYALTY_EXCHANGE_READ |
GET /v1/admin/data-exchange/feeds/customers\|operations\|accounts, GET /v1/admin/data-exchange/jobs, GET /v1/admin/data-exchange/jobs/runs |
LOYALTY_EXCHANGE_WRITE |
PATCH /v1/admin/data-exchange/jobs/{code}, POST /v1/admin/data-exchange/jobs/{code}/runs |
Интеграции¶
| Роль | Метод и путь |
|---|---|
LOYALTY_INTEGRATION_READ |
GET /v1/integration/pos/cards/{cardId}, GET /v1/integration/checkscan/customers/{externalId}, GET /v1/integration/customers/{externalId}/segments, POST /v1/integration/rtflow/condition-checks |
LOYALTY_INTEGRATION_WRITE |
POST /v1/integration/pos/receipts, POST /v1/integration/pos/returns, POST /v1/integration/pos/returns/{returnId}/cancel, POST /v1/integration/rtflow/accruals, POST /v1/integration/promo/accruals, POST /v1/integration/promo/accruals/reversals, POST /v1/integration/partner/transactions, POST /v1/admin/reconciliation/external-totals |
Гейтинг прав в UI (RBAC)¶
С выпуска 26.0.0 ролевая модель явно отражается в интерфейсе: все кнопки изменяющих действий
(create / edit / merge / назначение уровня / импорт акций / настройка лимитов и т. п.) неактивны
у пользователя без соответствующей роли LOYALTY_*_WRITE (для запуска акции —
LOYALTY_PROMOTION_ACTIVATE, для согласования — LOYALTY_PROMOTION_APPROVE, для активации
конфигурации — LOYALTY_PROGRAM_CONFIG_ACTIVATE). На недоступной кнопке показывается тултип-подсказка
с указанием требуемой роли.
Разделы с собственной read-ролью закрываются целиком экраном «Нет доступа к разделу» с перечнем недостающих ролей вместо попытки загрузки данных:
| Раздел | Нужная роль |
|---|---|
| «Акции» | LOYALTY_PROMOTION_READ |
| «Клиенты» | LOYALTY_CUSTOMER_READ |
| «Ручные операции» | Хотя бы одна из LOYALTY_ACCRUAL_WRITE, LOYALTY_REDEMPTION_WRITE, LOYALTY_TIER_WRITE, LOYALTY_CUSTOMER_TAG_WRITE (write-driven экран: вкладки операций без права скрыты) |
| «Конфигурации программы» | LOYALTY_PROGRAM_CONFIG_READ |
| «Мониторинг», «Отчёты» | LOYALTY_MONITORING_READ |
| «Справочники» → «Теги клиентов» / «Метки акций» / «Сегменты клиентов» | LOYALTY_TAG_READ / LOYALTY_PROMOTION_LABEL_READ / LOYALTY_SEGMENT_READ |
У «Настройки ПЛ» и подразделов «Администрирования» общей проверки на входе нет: они собраны из
блоков под разными ролями, и каждый блок сам сообщает об отказе. Это упреждающая проверка для удобства
оператора; итоговая проверка прав выполняется на бэкенде — при отсутствии роли API возвращает
403 независимо от состояния UI.
UI-гейтинг не заменяет серверную проверку
Неактивная кнопка или закрытый раздел в интерфейсе — это подсказка, а не граница
безопасности. Источником истины остаётся @PreAuthorize на бэкенде: даже прямой вызов
API без нужной роли будет отклонён (403).
Группа LOYALTY_ADMINS¶
В Keycloak заведена одна группа LOYALTY_ADMINS, объединяющая все роли LOYALTY_*, кроме
технических, которые предназначены интеграционным клиентам, а не операторам из Core UI:
LOYALTY_INTEGRATION_READLOYALTY_EXCHANGE_READ,LOYALTY_EXCHANGE_WRITELOYALTY_SEGMENT_INTAKE— выдаётся только учётной записи Batch Flow
Членство в LOYALTY_ADMINS — стартовая точка для реального заказчика (полный доступ
оператора/администратора), а не рекомендация выдавать всё разом каждому пользователю: набор
внутри группы дальше сузить нельзя, поэтому для персон с ограниченным доступом (см. ниже)
роли выдаются точечно, а не через группу.
Предупреждения о повторном провижининге realm¶
Часть ролей заведена в Keycloak позже базового набора. Если realm уже провижинен старой
версией import-loyalty-roles*.cfg, до перезапуска import.sh соответствующая
функциональность будет отвечать 403 access_denied, даже когда бэкенд обновлён:
| Роли | Что не будет работать до перезапуска import.sh |
|---|---|
LOYALTY_TAG_READ/WRITE, LOYALTY_CUSTOMER_TAG_WRITE, LOYALTY_PROMOTION_LABEL_READ/WRITE |
Справочник тегов, справочник меток акций, присвоение тегов клиентам, простановка меток на акции |
LOYALTY_INTAKE_READ/WRITE |
Экран «Очередь приёма» — «Вернуть в работу», «Отменить» |
LOYALTY_TIER_WRITE |
Ручное назначение уровня клиенту, перевод уровня в «Ручных операциях», параметры уровня |
LOYALTY_PROMOTION_ACTIVATE, LOYALTY_PROMOTION_APPROVE |
Запуск акции (одиночный и массовый), согласование запуска |
LOYALTY_PROGRAM_CONFIG_READ/WRITE/ACTIVATE |
Раздел «Конфигурации программы» целиком |
LOYALTY_SEGMENT_READ/WRITE/INTAKE |
Экран «Сегменты клиентов», приём сегментов из Batch Flow |
LOYALTY_CITY_*, LOYALTY_STORE_*, LOYALTY_MERCHANT_*, LOYALTY_PRODUCT_CATALOG_* |
Справочники городов, торговых точек, мерчантов и каталога товаров |
LOYALTY_CLIENT_PROFILE_ACTIVATE, LOYALTY_CLIENT_PROFILE_DELETE |
Активация и удаление версий настройки карточки клиента |
LOYALTY_ACHIEVEMENT_READ/WRITE |
Экран «Достижения», пересчёт бейджей клиента |
LOYALTY_NOTIFICATION_READ/WRITE |
Разбор очереди исходящих уведомлений |
BREAKING: запуск акции выведен из-под LOYALTY_PROMOTION_WRITE
POST /v1/promotions/{id}/activate и POST /v1/promotions/bulk-activate требуют
LOYALTY_PROMOTION_ACTIVATE — всегда, независимо от того, включено ли согласование.
До выкатки перезапустите import.sh (роли LOYALTY_PROMOTION_ACTIVATE и
LOYALTY_PROMOTION_APPROVE заводятся на клиенте frontend и попадают в LOYALTY_ADMINS) и
выдайте LOYALTY_PROMOTION_ACTIVATE группам, которые сегодня запускают акции правом записи, —
иначе «Запустить» будет отвечать 403 access_denied. LOYALTY_PROMOTION_APPROVE выдаётся
согласующим — другим людям, чем авторы акций.
BREAKING: LOYALTY_CORRECTION_OTHER_WRITE → LOYALTY_OPERATION_REASON_OTHER_WRITE
Роль, дающая право на причину «Прочее» в ручных операциях, переименована решением
продукта от 2026-09-11 (право относится к значению справочника «Прочее», а не только к
корректировке — им уже пользуются и начисление, и перевод уровня). На realm, провижиненном
со старым именем LOYALTY_CORRECTION_OTHER_WRITE, старую роль нужно снять вручную —
перезапуск import.sh только заводит новую LOYALTY_OPERATION_REASON_OTHER_WRITE, не
переименовывает существующую. Пока это не сделано, любая ручная операция (одиночная и
пакетная) с причиной «Прочее» будет отдавать 403 access_denied тем, у кого была только
старая роль.
Рекомендуемые наборы ролей по персонам¶
Принцип наименьших привилегий
Выдавайте пользователю только те роли, которые нужны для его задач.
| Персона | Роли |
|---|---|
| Оператор поддержки клиентов | LOYALTY_CUSTOMER_READ, LOYALTY_CUSTOMER_WRITE, LOYALTY_PROFILE_READ, LOYALTY_ACCOUNT_READ, LOYALTY_MONITORING_READ (карточка клиента требует его вместе с PROFILE_READ), LOYALTY_TAG_READ, LOYALTY_CUSTOMER_TAG_WRITE |
| Оператор ручных операций | Набор выше плюс любые из LOYALTY_ACCRUAL_WRITE / LOYALTY_REDEMPTION_WRITE / LOYALTY_TIER_WRITE / LOYALTY_CUSTOMER_TAG_WRITE по кругу разрешённых операций; для операций с баллами и уровнем обязательно LOYALTY_DICTIONARY_READ (иначе форма причины не откроется); LOYALTY_OPERATION_REASON_OTHER_WRITE — только доверенным операторам |
| Маркетолог | LOYALTY_PROMOTION_READ, LOYALTY_PROMOTION_WRITE, LOYALTY_PROMOTION_LABEL_READ, LOYALTY_PROMOTION_LABEL_WRITE (по умолчанию — обе на метки), LOYALTY_TAG_READ (условия по тегам), LOYALTY_SEGMENT_READ (экран сегментов; вход в него из реестра справочников требует ещё LOYALTY_DICTIONARY_READ), LOYALTY_ACHIEVEMENT_READ, LOYALTY_ACHIEVEMENT_WRITE; LOYALTY_PROMOTION_ACTIVATE — тем, кто запускает акции |
| Согласующий акций | LOYALTY_PROMOTION_READ, LOYALTY_PROMOTION_APPROVE — не тем же людям, что готовят акции |
| Администратор программы | LOYALTY_PROGRAM_CONFIG_READ, LOYALTY_PROGRAM_CONFIG_WRITE, LOYALTY_PROGRAM_CONFIG_ACTIVATE (активацию можно выдать отдельно), LOYALTY_TIER_READ, LOYALTY_TIER_WRITE, LOYALTY_PROMOTION_READ; LOYALTY_PROMOTION_WRITE — если конфигурация назначает акции уровням |
| Аналитик / дежурная смена мониторинга | LOYALTY_MONITORING_READ (мониторинг и раздел «Отчёты») |
| Администратор НСИ | LOYALTY_DICTIONARY_READ, LOYALTY_DICTIONARY_WRITE, LOYALTY_TAG_WRITE, LOYALTY_PROMOTION_LABEL_WRITE, LOYALTY_SEGMENT_READ, LOYALTY_SEGMENT_WRITE, LOYALTY_CITY_*, LOYALTY_STORE_*, LOYALTY_MERCHANT_*, LOYALTY_PRODUCT_CATALOG_*; LOYALTY_CLIENT_PROFILE_ACTIVATE и LOYALTY_CLIENT_PROFILE_DELETE — тем, кто применяет и чистит версии настройки карточки клиента |
| Администратор / эксплуатация | LOYALTY_BUSINESS_DAY_WRITE, LOYALTY_MAINTENANCE_WRITE, LOYALTY_INTAKE_READ, LOYALTY_INTAKE_WRITE, LOYALTY_NOTIFICATION_READ, LOYALTY_NOTIFICATION_WRITE |
| Интеграция POS / Check Scan / RT Flow | LOYALTY_INTEGRATION_READ, LOYALTY_INTEGRATION_WRITE, LOYALTY_EVENT_WRITE, LOYALTY_INTAKE_WRITE — по фактически используемым эндпоинтам конкретного клиента, не все сразу |
| Интеграция Batch Flow (сегменты) | LOYALTY_SEGMENT_INTAKE |
| Интеграция обмена данными / DWH | LOYALTY_EXCHANGE_READ, LOYALTY_EXCHANGE_WRITE |
Гранулярность денежных операций — в дорожной карте
Сейчас пополнение, списание, перевод и обмен баллов между счетами леджера (/top-up,
/withdraw, /transfer, /exchange) разделяют единую роль LOYALTY_OPERATION_WRITE.
При необходимости её можно разбить на отдельные роли по типам операций без изменения
существующего контракта.