Skip to content

GSSO — Записи архитектурных решений

Код документаSPEC-GSSO-2026 / Отчёт 01
Версия0.2 (исходный текст EN, имеющий преимущественную силу)
Дата2026-10-06
СтатусПринято. GSSO-ADR-001..014 имеют статус Acceptat (решение владельца, 2026-10-06); владелец может инициировать изменения, которые оформляются новым замещающим ADR
Сопутствующие отчёты00 RFP · 02 Требования · 03 Потребители и контракт · 04 Техническая документация · 05 Дорожная карта
ВерсияДатаИзменения
0.1-draft2026-10-05GSSO-ADR-001..014, все Proposed
0.22026-10-06GSSO-ADR-001..014 приняты владельцем (Acceptat); пересмотр отдельных ADR возможен через новые замещающие ADR
0.32026-10-06Выводы по реализации S1: GSSO-ADR-015 (изменяет 008) и GSSO-ADR-016 (уточняет 004), оба Proposed

ADR фиксирует одно значимое решение:

  • контекст: ситуация, потребовавшая принятия решения;
  • решение: что было выбрано;
  • рассмотренные альтернативы: что было отклонено и почему;
  • последствия: положительные и отрицательные эффекты, которые принимает команда.

Формат следует принятому стилю gregistry/docs/adr и paas_gflow/docs/reports/01-adr.ru.md. После принятия ADR неизменяемо, и любое изменение требует нового ADR, которое его заменяет.

Вышестоящие решения, которые необходимо соблюдать:

  • saas_crm/docs/reports/DECISIONS.md: D9 (GSSO — платформа идентификации gStack), D27/D28 (GSSO — существующий PaaS, подлежащий повторному использованию), D33 (стек DEV-PLAYBOOK §2 для каждого приложения).
  • gregistry/docs/adr/ADR-002-auth-oidc-gsso.md: никаких локальных паролей, роли в GSSO, SPA использует PKCE, M2M использует client credentials.
IDНазваниеСтатус
GSSO-ADR-001GSSO — это control plane поверх Keycloak, а не новый поставщик идентификацииAcceptat
GSSO-ADR-002Гибридная модель realm: gstack (персонал), cetatean (граждане), tenant-* (изолированные)Acceptat
GSSO-ADR-003Один кластер Keycloak 26.6.x с собственной PostgreSQL; существующий экземпляр сохраняетсяAcceptat
GSSO-ADR-004Согласование с желаемым состоянием (desired-state reconciliation) через Keycloak Admin REST APIAcceptat
GSSO-ADR-005Роли с пространством имён платформы и плоский claim rolesAcceptat
GSSO-ADR-006Доступ предоставляется через AtribuireAcces с жизненным циклом и принципом «четырёх глаз»Acceptat
GSSO-ADR-007События Keycloak через SPI event listener в Kafka; хранилище только на добавление; пересылка в GLogAcceptat
GSSO-ADR-008Пользователи остаются в Keycloak; GSSO хранит проекцию для чтенияAcceptat
GSSO-ADR-009Единый шаблон интеграции для приложений: gsso-spring-boot-starter и @gstack/gsso-angularAcceptat
GSSO-ADR-010Стек DEV-PLAYBOOK: микросервис JHipster 9.1.0 gsso + шлюз gsso-gateway + gsso-webAcceptat
GSSO-ADR-011Конфигурация как код: базовая конфигурация realm, расширения и темы в репозиторииAcceptat
GSSO-ADR-012Token exchange (RFC 8693, стандартный token exchange V2 в Keycloak) для вызовов от имени пользователя (on-behalf-of)Acceptat
GSSO-ADR-013Отзыв в течение 60 с: короткие токены, завершение сессий, события отзываAcceptat
GSSO-ADR-014Классификация по EU AI Act: GSSO не является системой ИИAcceptat
GSSO-ADR-015Операции с пользователями — прямые аудируемые вызовы Admin API (изменяет GSSO-ADR-008)Proposed
GSSO-ADR-016Реализация reconciler: тонкий клиент Admin REST, маркеры владения, облегчённый токен (уточняет GSSO-ADR-004)Proposed

GSSO-ADR-001 — GSSO — это control plane поверх Keycloak, а не новый поставщик идентификации

Section titled “GSSO-ADR-001 — GSSO — это control plane поверх Keycloak, а не новый поставщик идентификации”

Статус: Acceptat (2026-10-06)

Контекст.

  • Keycloak 26.6.3 уже обслуживает все приложения gStack по адресу sso.gstack.esempla.systems. Это сертифицированный поставщик OIDC/SAML с MFA, WebAuthn, брокерингом, федерацией LDAP, token exchange и консолью учётной записи.
  • Не хватает управления (governance):
    • каталога сервисов;
    • ролей в рамках платформы;
    • согласования доступа;
    • контроля расхождений (drift);
    • долговременного аудита;
    • единого стандарта интеграции;
    • консоли в дизайн-системе gStack.
  • Дизайн GSSO.dc.html представляет GSSO как «Single Sign-On (Keycloak)».

Решение.

  • GSSO — это control plane вокруг встроенного Keycloak.
  • Keycloak отвечает за аутентификацию, учётные данные, сессии, выпуск токенов, MFA, федерацию, интерфейс входа (с темой GDS) и самообслуживание учётной записи.
  • GSSO отвечает за:
    • управляемую модель (realm, платформы, клиенты, роли платформ, предоставления доступа, организационные единицы, политики аутентификации);
    • её согласование (reconciliation) с Keycloak;
    • хранилище событий;
    • отчёты;
    • API app для потребителей;
    • консоль администрирования.
  • GSSO никогда не обрабатывает пароли пользователей и никогда не выпускает токены.

Рассмотренные альтернативы.

  1. Использовать только штатную консоль администрирования Keycloak. Отклонено: нет каталога, нет согласования, нет контроля расхождений, нет GDS, нет аудита сверх собственного краткосрочного хранилища событий Keycloak.
  2. Написать собственного поставщика идентификации (Spring Authorization Server). Отклонено: пришлось бы заново реализовывать сертифицированные, критичные для безопасности функции (MFA, WebAuthn, SAML broker, LDAP) с высоким риском и затратами.
  3. Коммерческий IAM (Okta, Entra ID). Отклонено: ограничения суверенитета и размещения для государственных данных, стоимость лицензий и уже сделанные вложения в Keycloak.

Последствия.

  • (+) Критичный для безопасности код остаётся в зрелом проекте, а GSSO остаётся небольшим.
  • (+) Существующие приложения продолжают работать; URL издателя (issuer) не меняется для принятых (adopted) realm.
  • (−) Два источника конфигурации могут расходиться, что смягчается согласованием (GSSO-ADR-004).
  • (−) GSSO зависит от совместимости Keycloak Admin API; обновления допускаются только после прохождения тестов (GSSO-NFR-OPS-004).

GSSO-ADR-002 — Гибридная модель realm

Section titled “GSSO-ADR-002 — Гибридная модель realm”

Статус: Acceptat (2026-10-06)

Контекст.

  • Сессии SSO в Keycloak привязаны к realm. Пользователь, вошедший в realm A, не авторизован в realm B, если B не использует A как брокер.
  • Сегодня realm interdictii де-факто является общим realm для персонала, тогда как другие имена realm (gdocs, gnotify, gstorage, gstack, ultra) встречаются в коде без какого-либо плана.
  • В макете указан один realm на систему (cancelaria, interdictii, portal-cetatean, glog-services). Это даёт изоляцию, но не даёт общего SSO.

Решение.

RealmКтоКлиентыФедерация
masterТолько начальная настройка Keycloak и аварийный доступ (break-glass)gsso-reconciler (service account)—
gstackВесь персонал gStack и операторы всех учрежденийВсе клиенты SaaS и PaaS для персонала, консоль GSSO, service accounts всех сервисовAD/LDAP для каждого учреждения (опционально), без MPass
cetateanГраждане и предприятияПубличные порталы и SPA для гражданMPass/eID (SAML broker), связывание при первом входе через брокер (first-broker-login) по IDNP
tenant-<code>Изолированные заказчики (whitelabel-сайты ultra-*, bts-*, esempla-* или учреждение, которому требуется собственный realm)Только клиенты данного заказчикаДля каждого tenant отдельно
  • Учреждения внутри gstack разделяются организационными единицами (claim org_unit) и ролями платформ, а не realm.
  • Realm для tenant создаются из версионируемого шаблона realm.
  • Переключатель realm в консоли задаёт область realm для каждого экрана.

Рассмотренные альтернативы.

  1. Realm на каждое приложение (как в макете). Отклонено для персонала: нет SSO между приложениями, роли и пользователи дублируются в каждом realm. Сохранено только для tenant-*.
  2. Единый realm для всего, включая граждан. Отклонено: у граждан и персонала разные политики (MFA, длительность сессии, федерация, минимизация данных), а административные интерфейсы для персонала не должны быть доступны с учётной записью гражданина.
  3. Keycloak Organizations (несколько организаций внутри одного realm). Рассматривалось для учреждений. Оставлено как вариант на будущее (Q-GSSO-1). Организационные единицы через атрибуты покрывают текущие потребности с меньшей привязкой к недавно появившейся функции.

Последствия.

  • (+) Единый вход во все приложения для персонала (цель G-1).
  • (+) Данные и политики граждан изолированы от персонала.
  • (−) Realm gstack становится критичным. Его доступность — это доступность всех приложений для персонала (GSSO-NFR-AVL-*).
  • (−) Существующие клиенты в interdictii должны мигрировать (отчёт 05, фаза S4), либо interdictii принимается как gstack (Q-GSSO-1).

GSSO-ADR-003 — Один кластер Keycloak 26.6.x с собственной PostgreSQL; существующий экземпляр сохраняется

Section titled “GSSO-ADR-003 — Один кластер Keycloak 26.6.x с собственной PostgreSQL; существующий экземпляр сохраняется”

Статус: Acceptat (2026-10-06)

Контекст.

  • Работающий Keycloak имеет версию 26.6.3 (контейнер keycloak, контейнер базы данных postgres-keycloak) и размещён на общем демонстрационном хосте за пограничным nginx.
  • В файле дизайна gstyle упоминается «Keycloak 24», но эта информация устарела.

Решение.

  • GSSO фиксирует Keycloak 26.6.x (обновления патч-версий допускаются) с выделенной базой данных PostgreSQL, отдельной от базы данных GSSO.
  • Репозиторий GSSO содержит определения compose и Helm, воспроизводящие работающий экземпляр:
    • KC_PROXY_HEADERS=xforwarded;
    • имя хоста sso.gstack.esempla.systems;
    • смонтированные JAR расширений и темы;
    • включённые health и metrics.
  • Работающий экземпляр принимается в управление на месте, а не заменяется. Сначала создаётся резервная копия его базы данных, затем расширения и тема GSSO добавляются в окне обслуживания, согласованном с владельцем.
  • Целевая конфигурация высокой доступности в production: 2 узла Keycloak со встроенным кластером Infinispan (сессии реплицируются) за ingress.

Рассмотренные альтернативы.

  • Новый Keycloak и миграция данных: отклонено. Это изменило бы секреты и идентификаторы пользователей (sub) и сломало бы все приложения.
  • Общая PostgreSQL с GSSO: отклонено, поскольку у них разные профили резервного копирования, обновления и радиуса поражения (blast radius).

Последствия.

  • (+) URL издателя и sub существующих пользователей не меняются.
  • (−) Приём в управление требует тщательного runbook (отчёт 04 §11) и точки отката.

GSSO-ADR-004 — Согласование с желаемым состоянием через Keycloak Admin REST API

Section titled “GSSO-ADR-004 — Согласование с желаемым состоянием через Keycloak Admin REST API”

Статус: Acceptat (2026-10-06)

Контекст.

  • GSSO должен приводить Keycloak в соответствие со своей управляемой моделью, переживать частичные сбои и выявлять ручные изменения.

Решение.

  • Путь записи: каждое изменение каталога или предоставления доступа фиксирует в одной транзакции изменение сущности GSSO и JobSincronizare (транзакционный outbox). Рабочий процесс reconciler забирает ожидающие задания (SELECT … FOR UPDATE SKIP LOCKED) и применяет их через keycloak-admin-client 26.x с service account gsso-reconciler.
  • Идемпотентность: каждая операция выполняет поиск по естественному ключу (имя realm, clientId, имя роли, id пользователя + роль), а затем создаёт, обновляет или ничего не делает. Внутренний id Keycloak сохраняется обратно как kcId.
  • Повторные попытки: экспоненциальная задержка (1 с → 5 мин, не более 10 попыток), затем FAILED, что вызывает оповещение через GNotify.
  • Полное согласование:
    • периодически (по умолчанию каждые 15 мин) и по запросу;
    • вычисляет для каждого realm разницу между желаемым состоянием и Keycloak, только для управляемых типов объектов;
    • результаты становятся строками JobSincronizare со stare=DRIFT и JSON-разницей.
  • Политика расхождений для каждого realm:
    • REPORT (по умолчанию): только отображать;
    • ENFORCE: исправлять автоматически;
    • IGNORE: для принятых realm во время миграции.
  • Маркер владения: объекты, созданные GSSO, имеют атрибут gsso.managed=true. Неуправляемые объекты отображаются как «unmanaged» и никогда не удаляются, если они не приняты в управление.
  • Принятие (adoption): импорт только для чтения клиентов, ролей и назначений ролей realm в каталог в виде Platforma + ClientAplicatie + RolPlatforma + AtribuireAcces(ACTIVA, sursa=ADOPTAT).

Рассмотренные альтернативы.

  1. Только keycloak-config-cli / Terraform: хорошо подходит для статических базовых конфигураций (используется в GSSO-ADR-011), но нет предоставления доступа во время выполнения, нет согласования и нет интерфейса.
  2. Прямая запись в базу данных Keycloak: отклонено как неподдерживаемое и небезопасное.
  3. Синхронные вызовы из интерфейса без заданий: отклонено, поскольку частичные сбои оставляют несогласованное состояние без повторных попыток.

Последствия.

  • (+) Устойчивость, наблюдаемость и возможность повторного воспроизведения.
  • (−) Интерфейс должен показывать статус синхронизации каждого объекта (stareSync).
  • (−) Согласованность в конечном счёте (eventual consistency): обычно менее 2 с, ограничена политикой повторных попыток.

GSSO-ADR-005 — Роли с пространством имён платформы и плоский claim roles

Section titled “GSSO-ADR-005 — Роли с пространством имён платформы и плоский claim roles”

Статус: Acceptat (2026-10-06)

Контекст.

  • Сегодня realm-роли ROLE_ADMIN и ROLE_USER общие для всех приложений.
  • Приложения считывают их из realm_access.roles или из пользовательского claim roles.
  • Группам-кандидатам gFlow нужны имена ролей с префиксом приложения (CRM_*, GTENDERS_*).

Решение.

  • Каждая роль, которую предоставляет платформа, — это realm-роль с именем <PLATFORMA_COD_UPPER>_<ROL> (например, INTERDICTII_EMITENT, CRM_ADMIN, GLOG_AUDITOR) и атрибутом gsso.platforma=<cod>.
  • Составные (composite) роли объединяют нижестоящие роли в пределах одной платформы.
  • Роль по умолчанию ROLE_USER сохраняется для обозначения «аутентифицированный пользователь из персонала».
  • ROLE_ADMIN объявлена устаревшей. Во время миграции она сопоставляется с <APP>_ADMIN для каждого использующего её приложения, затем удаляется (отчёт 05 S4).
  • Клиентские роли допускаются для областей service account (например, gstorage-service: object:read). Авторизация людей использует только realm-роли, чтобы один claim roles обслуживал все приложения.
  • Mapper GSSO формирует плоский массив roles (realm-роли пользователя, действующие с учётом составных ролей).
  • Client scope gsso-roles-filtered может ограничить массив ролями платформы-получателя (audience) токена плюс ROLE_USER, чтобы токены оставались небольшими.
  • Starter сопоставляет roles с authorities Spring. <APP>_X становится authority <APP>_X; для совместимости с JHipster приложение может локально сопоставить <APP>_ADMIN с ROLE_ADMIN.

Рассмотренные альтернативы.

  • Клиентские роли для каждого приложения для людей: отклонено. Тогда токенам нужна структура claim для каждого клиента (resource_access), а группы-кандидаты gFlow и запросы между приложениями усложняются.
  • Группы как роли: группы по-прежнему используются, но только как способ объединения ролей (например, «Operator Cancelaria»). Авторизация всегда выполняется по ролям.

Последствия.

  • (+) Чёткий радиус поражения для каждой платформы (цель G-2).
  • (+) Один claim для всех приложений.
  • (−) Существующие приложения должны переименовать свои проверки ролей, что смягчается картой псевдонимов starter во время миграции.

GSSO-ADR-006 — Доступ предоставляется через AtribuireAcces с жизненным циклом и принципом «четырёх глаз»

Section titled “GSSO-ADR-006 — Доступ предоставляется через AtribuireAcces с жизненным циклом и принципом «четырёх глаз»”

Статус: Acceptat (2026-10-06)

Контекст.

  • Назначения ролей, сделанные непосредственно в Keycloak, не содержат ни причины, ни согласующего, ни срока действия.
  • NFRQ56–66 требуют минимальных привилегий и аудита безопасности.
  • CAP-GSSO-04 требует быстрого отзыва.

Решение.

  • Роль платформы для человека назначается только через AtribuireAcces:
SOLICITATA ──approve──▶ APROBATA ──reconciled──▶ ACTIVA ──revoke──▶ REVOCATA
│ │ └──validPana passed──▶ EXPIRATA
└──reject──▶ RESPINSA └──second approval required (sensitive)──▶ stays APROBATA(1/2)
  • Правила, обеспечиваемые на сервисном уровне; каждый переход записывает EvenimentGsso:
    • Запрашивающим может быть сам пользователь, его руководитель, владелец платформы или приложение через api/v1/app. Указание причины обязательно.
    • Согласующий — владелец платформы или администратор realm. Для чувствительных ролей дополнительно требуется второй, отличный от первого согласующий с GSSO_APPROVER. Никто не согласует собственный запрос.
    • ACTIVA устанавливается только после того, как reconciler подтвердит назначение в Keycloak.
    • validPana необязателен. Его значение по умолчанию берётся из политики роли (Q-GSSO-5). Ежечасный планировщик переводит в истёкшие предоставления, срок которых наступил. Пользователи получают предупреждение об истечении срока за 14 дней и за 1 день через GNotify.
    • Отзыв и истечение срока удаляют назначение и запускают GSSO-ADR-013.
    • Администраторы могут предоставлять доступ напрямую (запрос и согласование за один шаг) только для нечувствительных ролей. Это фиксируется в аудите как ACORDARE_DIRECTA.
  • Service accounts получают роли через конфигурацию ClientAplicatie, а не через предоставления доступа.

Рассмотренные альтернативы.

  • Назначение ролей только в Keycloak: нет управления.
  • Процесс BPMN в gFlow: рассматривался. GSSO является зависимостью gFlow (gFlow аутентифицируется через GSSO), поэтому возникла бы циклическая зависимость. Жизненный цикл невелик, и машины состояний на сервисном уровне достаточно.

Последствия.

  • (+) Даёт ответ на вопрос «у кого что есть, почему, с какого момента, до какого момента и кем согласовано».
  • (−) Добавляет шаг в повседневную административную работу, что смягчается массовыми предоставлениями доступа и шаблонами ролей (группами).

GSSO-ADR-007 — События Keycloak через SPI event listener в Kafka; хранилище только на добавление; пересылка в GLog

Section titled “GSSO-ADR-007 — События Keycloak через SPI event listener в Kafka; хранилище только на добавление; пересылка в GLog”

Статус: Acceptat (2026-10-06)

Контекст.

  • Keycloak хранит пользовательские и административные события в собственной базе данных с коротким сроком хранения и не поддерживает push-доставку.
  • Они нужны для панели мониторинга, событий безопасности, времени последнего входа и проекции.
  • GLog — WORM-хранилище аудита gStack.

Решение.

  • Event listener. Расширение Keycloak от GSSO предоставляет EventListenerProvider gsso-kafka. Оно публикует каждое пользовательское событие (LOGIN, LOGIN_ERROR, LOGOUT, CODE_TO_TOKEN, CLIENT_LOGIN, UPDATE_CREDENTIAL, REGISTER, IDENTITY_PROVIDER_*…) и административное событие (с представлением объекта) в topic Kafka gsso.kc-events.v1:
    • CloudEvents в бинарном режиме;
    • ключ = realmId:userId;
    • асинхронно, с ограниченным локальным буфером.
    • Если Kafka недоступна, событие всё равно остаётся в хранилище событий Keycloak, а пропуск восполняется из Admin API потребителем GSSO.
  • Потребитель. gsso идемпотентно потребляет topic (дедупликация по eventId), записывает EvenimentGsso и обновляет ProiectieUtilizator (последний вход, неудачные попытки) и агрегаты панели мониторинга.
  • Собственные события GSSO. Изменения каталога, переходы предоставлений доступа и задания синхронизации записываются в EvenimentGsso в той же транзакции, что и изменение.
  • Хранилище. EvenimentGsso — только на добавление (append-only):
    • у репозитория нет методов обновления или удаления;
    • триггер базы данных отклоняет UPDATE и DELETE;
    • каждая строка содержит hashPrecedent и hash (цепочка SHA-256 для каждого realm).
  • Пересылка. Ретранслятор (relay) пересылает каждое событие в GLog POST /api/v1/app/audit-events (client credentials, scope audit:write) с повторными попытками и записывает id квитанции GLog.
  • Срок хранения. 90 дней в GSSO (Q-GSSO-7); долговременный журнал хранится в GLog.

Рассмотренные альтернативы.

  • Опрос API событий Keycloak: отклонено в качестве основного пути из-за задержки и стоимости постраничной выборки. Сохранено для восполнения пропусков.
  • Сторонние listener для Kafka: отклонено. Нужны оболочка CloudEvents и именование topic по правилам gStack.

Последствия.

  • (+) Панель мониторинга почти в реальном времени и журнал для расследований.
  • (−) Kafka добавляется в зависимости среды выполнения Keycloak. Сбой Kafka не блокирует вход, поскольку listener асинхронный и неблокирующий.

GSSO-ADR-008 — Пользователи остаются в Keycloak; GSSO хранит проекцию для чтения

Section titled “GSSO-ADR-008 — Пользователи остаются в Keycloak; GSSO хранит проекцию для чтения”

Статус: Acceptat (2026-10-06)

Контекст.

  • У пользователей и учётных данных должен быть один мастер-источник. Консоли нужны быстрый поиск по всем realm, время последнего входа и статус MFA, а также просмотр предоставлений доступа по пользователю.

Решение.

  • Keycloak — мастер-источник пользователей: идентичность, атрибуты, учётные данные, связи федерации.
  • Редактирование пользователей в консоли вызывает Keycloak Admin API через reconciler (JobSincronizare типа UTILIZATOR). Сброс учётных данных и обязательные действия (required actions) вызывают его синхронно, поскольку они не являются желаемым состоянием.
  • ProiectieUtilizator обновляется тремя способами:
    • из событий;
    • ночной полной синхронизацией для каждого realm (постранично, по 500 на страницу);
    • по запросу при открытии пользователя. Она хранит sub, имя пользователя, имя, e-mail, idnp (в зашифрованном виде), признак enabled, типы MFA, время последнего входа и realm.
  • Удаление пользователя в Keycloak очищает проекцию и анонимизирует поля действующего лица в рабочих таблицах GSSO. Строки аудита сохраняют только sub.

Последствия.

  • (+) Нет двойного мастер-источника; пользователи из федерации (LDAP/MPass) видны.
  • (−) Проекция может отставать на несколько секунд; консоль показывает время «обновлено в».

GSSO-ADR-009 — Единый шаблон интеграции для приложений

Section titled “GSSO-ADR-009 — Единый шаблон интеграции для приложений”

Статус: Acceptat (2026-10-06)

Контекст.

  • Сегодня существуют четыре шаблона (отчёт 00 §2).
  • Шаблон A — JWT HS512, выпускаемые приложением после входа через Keycloak, — сохраняет локальный ключ подписи и локальные пароли. Это лишает смысла единый выход (SSO logout), отзыв и аудит.

Решение.

Тип приложенияШаблон
Шлюз JHipster + микросервисы (playbook)Шлюз = OAuth2 client (BFF, authorization code + PKCE, конфиденциальный). Микросервисы = resource servers. Всё через gsso-spring-boot-starter
Монолит Spring Boot с SPAResource server через starter; SPA = публичный клиент + PKCE через @gstack/gsso-angular (или режим BFF)
Межсервисное взаимодействиеClient credentials, один конфиденциальный клиент на каждый вызывающий сервис; token exchange для вызовов от имени пользователя (GSSO-ADR-012)
Клиенты KafkaSASL OAUTHBEARER с client credentials сервиса
Мобильное приложение (gsso_mob)Публичный клиент + PKCE + redirect с пользовательской схемой
Сервисы не на Java (Python RAG, Node)Проверка через JWKS; документированный фрагмент кода
  • Starter (systems.esempla.gsso:gsso-spring-boot-starter):
    • проверка issuer и JWKS плюс проверка audience;
    • roles → authorities, с необязательной картой псевдонимов для миграции;
    • GssoPrincipal, предоставляющий sub, username, orgUnit, locale, idnp?;
    • перехватчик client credentials для RestClient/WebClient и помощник для token exchange;
    • listener событий отзыва (GSSO-ADR-013);
    • KeycloakHealthIndicator в группе readiness;
    • помощник для Testcontainers. Он повторно использует проверенный код из gregistry (AudienceValidator) и cancelarie (KeycloakHealthIndicator).
  • Angular-библиотека (@gstack/gsso-angular): вход через PKCE (или режим сессии BFF), guard ролей и директива *gssoHasRole, тихое обновление (silent refresh), единый выход и синхронизация локали из claim locale.
  • Локальные пароли и токены, выпускаемые приложением, запрещены для новых приложений и удаляются из существующих в ходе миграции (отчёт 03 §2).

Последствия.

  • (+) Одна проверка безопасности охватывает все приложения; выход и отзыв работают везде.
  • (−) Приложения с шаблоном A (interdictii, gdocs, gnotify) требуют изменений в коде, запланированных в отчёте 05.

Статус: Acceptat (2026-10-06)

Решение.

  • gsso: JHipster 9.1.0, applicationType: microservice.
    • Пакет systems.esempla.gsso, authenticationType: oauth2 (сам GSSO, realm gstack), Maven, PostgreSQL, Kafka.
    • languages: ro,ru,en с основным языком ro, skipClient: true.
    • Сущности из gsso.jdl.
  • gsso-gateway: JHipster 9.1.0 applicationType: gateway. Это BFF для gsso-web (клиент gsso-console, конфиденциальный) и единая точка входа API; он маршрутизирует только /api/v1/** и /api/v1/app/**.
  • gsso-web: отдельный Angular SPA на @gstack/gds-angular / @gstack/gds-core, с темой gdsThemedCss('#7c3aed') (--gds-accent-gsso), i18n ro/ru/en.
  • keycloak/: расширения (модуль Maven, Java 21, Keycloak SPI 26.6.x), темы (FreeMarker + GDS CSS) и базовые конфигурации realm.
  • Версии Java, Spring Boot, Angular, PostgreSQL и Kafka — ровно те, что генерирует JHipster 9.1.0, с фиксацией на S0. Любое отклонение требует ADR.
  • Начальная загрузка: консоль аутентифицируется в том самом Keycloak, которым она управляет. Если GSSO недоступен, Keycloak и все входы продолжают работать; приостанавливается только управление.

Последствия.

  • (+) Тот же набор инструментов и те же соглашения, что и у других приложений gStack.
  • (−) Консоль зависит от realm gstack. Аварийный доступ (break-glass) — консоль администрирования Keycloak в realm master, доступная только команде платформы.

GSSO-ADR-011 — Конфигурация как код

Section titled “GSSO-ADR-011 — Конфигурация как код”

Статус: Acceptat (2026-10-06)

Решение.

  • Репозиторий содержит следующее; всё это применяется при развёртывании разовым заданием gsso-bootstrap с помощью keycloak-config-cli, с подстановкой переменных и без секретов:
    • keycloak/realms/gstack.json, cetatean.json и tenant-template.json: базовая конфигурация, то есть настройки realm, потоки (flows), client scopes, mappers, политика паролей, темы, event listener, а также клиенты gsso-console и gsso-reconciler;
    • keycloak/extensions/ (JAR);
    • keycloak/themes/gstack/ (login, account, email);
    • docker-compose.yml и helm/.
  • Объекты времени выполнения (платформы, их клиенты и роли, предоставления доступа, пользователи) принадлежат reconciler GSSO, а не файлам базовой конфигурации. Эти два множества не пересекаются, поэтому начальная загрузка никогда не перезаписывает объекты времени выполнения.
  • Секреты (секреты клиентов, учётные данные reconciler, SMTP, привязка LDAP) поступают из переменных окружения или хранилища секретов. .env.example перечисляет только имена.

Последствия.

  • (+) Keycloak можно восстановить из git плюс резервной копии базы данных (цель G-4), а изменения проверяются в merge request.

GSSO-ADR-012 — Token exchange для вызовов от имени пользователя

Section titled “GSSO-ADR-012 — Token exchange для вызовов от имени пользователя”

Статус: Acceptat (2026-10-06)

Контекст.

  • CAP-GSSO-07: вызовы CRM ↔ gDocFlow ↔ gTenders должны передавать идентичность пользователя, чтобы вызываемое приложение применяло правила видимости этого пользователя.

Решение.

  • Использовать стандартный token exchange (V2, internal-to-internal) Keycloak, доступный в 26.x.
  • У каждой целевой платформы есть client scope audience.
  • Клиент-источник может выполнять обмен только для audience, перечисленных в его ClientAplicatie.audienteSchimb. GSSO согласует эти списки с разрешениями token exchange или политиками клиентов.
  • Полученные в результате обмена токены:
    • сохраняют sub пользователя;
    • устанавливают aud равным целевой платформе;
    • содержат act.sub = вызывающий клиент;
    • ограничены ролями целевой платформы.
  • Имперсонация и обмен external-to-internal отключены.

Последствия.

  • (+) Делегирование с минимальными привилегиями без передачи паролей пользователей или долгоживущих токенов.
  • (−) Разрешённые пары необходимо поддерживать; они видны в консоли и в отчёте 03.

GSSO-ADR-013 — Отзыв в течение 60 с

Section titled “GSSO-ADR-013 — Отзыв в течение 60 с”

Статус: Acceptat (2026-10-06)

Контекст.

  • CAP-GSSO-03/04 требуют, чтобы отозванный пользователь или роль теряли доступ в течение 60 с (в некоторых пакетах требование смягчено до 5 мин).
  • Resource servers проверяют JWT офлайн, поэтому отозванная роль остаётся в ещё не истёкшем access token.

Решение.

  • Время жизни access token: 5 мин в gstack; 2 мин для клиентов с пометкой sensitive.
  • При отзыве, истечении срока, отключении пользователя или «завершении сессий» GSSO:
    1. удаляет назначение или отключает пользователя;
    2. удаляет сессии пользователя в Keycloak (logout пользователя, что также запускает back-channel logout для клиентов, которые его поддерживают);
    3. публикует в Kafka gsso.access-revoked.v1 с sub, затронутыми ролями платформ и временем not-before.
  • Starter потребляет gsso.access-revoked.v1 и хранит в памяти список запретов с ключом sub + not-before на время жизни токена. Запрос с токеном, выпущенным до времени not-before, получает 401.
  • Шлюзы BFF также удаляют свою серверную сессию при back-channel logout.

Рассмотренные альтернативы.

  • Интроспекция токена при каждом запросе: отклонено из-за задержки и нагрузки на Keycloak со стороны всех приложений. Остаётся доступной для особо чувствительных endpoint.

Последствия.

  • (+) Отзыв в течение 60 с для приложений, использующих starter. Остальные полагаются на истечение срока токена (максимум 5 мин).

GSSO-ADR-014 — Классификация по EU AI Act

Section titled “GSSO-ADR-014 — Классификация по EU AI Act”

Статус: Acceptat (2026-10-06)

Решение.

  • GSSO не является системой ИИ в смысле Регламента (ЕС) 2024/1689, ст. 3(1). Он не содержит логического вывода (inference), скоринга или ранжирования лиц.
  • Вход с учётом риска (адаптивная MFA по сигналам IP или устройства), если он будет добавлен позже, должен быть основан на правилах и задокументирован. Любой скоринг на основе ML требует новой классификации с помощью навыка ai-act до начала проектирования.

GSSO-ADR-015 — Операции с пользователями — прямые аудируемые вызовы Admin API

Section titled “GSSO-ADR-015 — Операции с пользователями — прямые аудируемые вызовы Admin API”

Статус: Proposed (2026-10-06). Изменяет одно предложение GSSO-ADR-008.

Контекст.

  • GSSO-ADR-008 предусматривает, что редактирование пользователей в консоли проходит через reconciler как JobSincronizare типа UTILIZATOR.
  • Пользователи не являются желаемым состоянием: их мастер-источник — Keycloak.
  • Создание пользователя должно сразу возвращать консоли его id в Keycloak, а администратор ожидает, что отключение или завершение сессий подействует сразу, а не после очереди.

Решение.

  • Операции с пользователями синхронно вызывают Keycloak Admin API через KeycloakAdminClient: создание, редактирование, включение/отключение, обязательные действия (required actions), удаление учётных данных, завершение сессий, разблокировка.
  • Каждая успешная операция добавляет EvenimentGsso в рамках того же запроса и обновляет ProiectieUtilizator.
  • Ошибки Keycloak отображаются в RFC 7807: 409 — конфликт, 400 — ошибка валидации, 502 — сбои Keycloak.
  • Очередь reconciler остаётся для желаемого состояния: realm, клиенты, роли и, начиная с S2, назначения ролей по предоставлениям доступа.

Последствия.

  • (+) Немедленная обратная связь и отсутствие очереди для операций, не являющихся желаемым состоянием.
  • (−) При недоступности Keycloak операции с пользователями сразу завершаются ошибкой, а не откладываются; консоль показывает ошибку.

GSSO-ADR-016 — Детали реализации reconciler

Section titled “GSSO-ADR-016 — Детали реализации reconciler”

Статус: Proposed (2026-10-06). Уточняет GSSO-ADR-004; принятые там решения остаются в силе.

Контекст. Разработка и тестирование S1 на Keycloak 26.6.3 выявили пять моментов, которые GSSO-ADR-004 оставил открытыми или определил неверно.

Решение.

  1. Admin-клиент. Тонкий JSON-клиент на Spring RestClient (KeycloakAdminClient) заменяет keycloak-admin-client. Стек RESTEasy этой библиотеки плохо уживается со Spring Boot 4, а GSSO использует лишь небольшую часть API. Все вызовы Keycloak по-прежнему проходят через этот единственный класс.
  2. Маркер владения. gsso.managed принимает два значения:
    • catalog: создан reconciler, который может обновлять и удалять объект;
    • baseline: принадлежит базовым файлам realm (baseline) или шаблону тенанта; reconciler никогда не изменяет и не удаляет его, а обнаружение расхождений его пропускает. Немаркированные объекты считаются неуправляемыми: о них сообщается, но они никогда не удаляются. Единственное значение true позволило бы режиму ENFORCE удалить клиент консоли и роль по умолчанию ROLE_USER в каждом realm тенанта.
  3. Облегчённый access token для gsso-reconciler. Опция client.use.lightweight.access.token.enabled. Полный токен содержит административные роли каждого realm, созданного reconciler, и после нескольких десятков realm превышает ограничения на размер HTTP-заголовков (HTTP 431). Кроме того, его claims устаревают сразу после создания realm (HTTP 403). С облегчённым токеном (≈780 байт, постоянный размер) Keycloak проверяет права в реальном времени. Ответ 403 по-прежнему вызывает одно обновление токена и одну повторную попытку.
  4. Порядок. Рабочий процесс забирает задания, срок которых наступил, с помощью FOR UPDATE SKIP LOCKED и никогда не берёт realm, у которого уже есть задание в статусе RUNNING, поэтому задания одного realm применяются по одному и в порядке id, в том числе при нескольких экземплярах (GSSO-FR-079).
  5. Классы сбоев.
    • Временные: нет ответа, 401, 429, 5xx. Повторяются с экспоненциальной задержкой (1 с → 5 мин), FAILED после 10 попыток.
    • Постоянные: прочие 4xx или объект, принадлежащий baseline. Сразу FAILED.

Последствия.

  • (+) Каждый пункт покрыт интеграционными тестами на реальном Keycloak (ReconcilerIT).
  • (−) Тонкий клиент приходится расширять вручную, когда нужны новые вызовы Admin API.