Zimin A.N.
930df5b888
feat(api): Swagger UI + docs/integration для Альтума и других consumer'ов
...
Springdoc-openapi генерирует OpenAPI 3 spec из аннотаций контроллеров —
интеграторам не нужно поддерживать markdown-ТЗ синхронно с кодом.
- /swagger-ui.html (UI), /v3/api-docs (JSON), /v3/api-docs.yaml.
- OpenApiConfig: title/description/contact/servers (staging+prod+local),
bearerAuth security scheme (Keycloak JWT).
- SecurityConfig: permitAll на swagger paths.
Документация:
- docs/integration/altum-imaging-order.md — ТЗ интеграции «Заказ съёмки».
Согласованы: m2m service account через Keycloak, BFF в altum-backend
(FastAPI), 4 справочника со схемами, cascading select через
instrument.supported_*_codes, snapshot strategy для аудита.
- docs/tech/api-integration.md — общий гайд для интеграторов: auth flow,
endpoints, кэш-стратегии, bitemporal модель, current dictionaries,
cross-refs, best practices.
Скрипты codegen openapi-generator-cli работают off /v3/api-docs.
2026-05-04 15:45:09 +03:00
Zimin A.N.
416c44bd94
test: unit-тесты для ScopeContext, IdempotencyService, DictionaryRecordService + fix exclusion 500 → 409
...
35 unit-тестов зелёных:
ordinis-auth (11): ScopeContextTest
- Маппинг ролей: ordinis-public/ORDINIS_INTERNAL/x-RESTRICTED/PUBLIC
- Nested claim path realm_access.roles + кастомный путь scopes
- Anonymous + query allowed/disallowed
- JWT всегда побеждает query as_scope (escalation defense)
- Unrecognized roles → PUBLIC fallback
ordinis-rest-api/idempotency (13): IdempotencyServiceTest
- hashRequest: deterministic, sha-256 hex 64 chars, null=empty
- lookup: empty/match/422 на mismatch
- reserve: saveAndFlush, race с тем же hash → 409 InProgress, race с разным hash → 422 Conflict
- saveResponse: skip missing, persist parsed JSON, gracefully игнорит non-JSON
ordinis-rest-api/service (11): DictionaryRecordServiceTest
- resolveBusinessKey: explicit / no-source missing key throws / x-id-source derive /
match accepted / mismatch throws / missing source field throws
- enforceUniqueFields: no-unique no-op / no-conflict / conflict 409 /
excludes own record on update / skips null values
Hotfix backend: exclusion constraint 500 → friendly 409
- DictionaryRecordService.create() / update() ловят DataIntegrityViolationException,
если SQLState 23P01 или message содержит dictionary_records_no_overlap →
OrdinisException.conflict('record_period_overlap', ...) с понятным сообщением
про пересекающийся диапазон.
- Saved → saveAndFlush: get DB error eagerly чтобы поймать его в catch.
Test setup:
- spring-boot-starter-test (test scope) в parent pom.
- Mockito mock для DictionaryDefinitionService на JDK 25 ломается, передаём null
(тестируемые методы не дёргают этот dependency).
2026-05-04 04:07:33 +03:00
Zimin A.N.
7593699990
feat(auth): refactor to Keycloak OIDC issuer-uri (drop static PEM)
...
@nstart/auth = Keycloak. Уходим от RSA PEM в Vault на OIDC discovery.
OrdinisAuthProperties:
- DROP: publicKey (PEM)
- ADD: issuerUri (https://auth.nstart.space/realms/nstart )
- ADD: jwkSetUri (optional override)
- ADD: audience (для aud валидации, когда узнаем client_id)
- RENAME: scopeClaim → rolesClaim (default: realm_access.roles)
SecurityConfig:
- JwtDecoders.fromIssuerLocation() — auto JWK Set discovery через
/.well-known/openid-configuration. Ротация ключей не требует ручных операций.
- AudienceValidator (опц.) для проверки aud claim.
- Permissive stub когда issuer-uri пуст (dev mode).
ScopeContext:
- readClaimByPath() поддерживает nested путь "realm_access.roles".
- normalizeRoleToScope(): маппит Keycloak роли ("ordinis-public",
"ORDINIS_INTERNAL", "x-RESTRICTED") в DataScope.
- JWT всегда побеждает query ?as_scope= (защита от scope escalation).
2026-05-04 02:13:08 +03:00
Александр Зимин
6ee7a70ad1
feat(auth): JWT scope authorization (@nstart/auth контракт)
...
Новый модуль ordinis-auth:
- OrdinisAuthProperties — public-key, issuer, requireAuthentication, allowQueryScope
- ScopeContext — резолвит current scopes из JWT claim 'scopes' (приоритет),
потом legacy ?as_scope=, потом anonymous=PUBLIC
- SecurityConfig — Spring Security OAuth2 Resource Server + RSA public-key
decoder. Если public-key пуст — JWT validation отключён (permissive
fallback); rollout без auth-сервера не ломает API.
Read controller теперь делегирует scope resolution в ScopeContext вместо
прямого парсинга ?as_scope=. Backward compat: legacy query параметр
работает пока ordinis.auth.allow-query-scope=true (по умолчанию true в
dev/staging, false в prod после интеграции с @nstart/auth).
Vault: public-key читается из secret/ordinis/cuod/jwt-public-key (key=key).
Spring Cloud Vault flatten'ит в property 'key', resolved в
ordinis.auth.public-key=${key:}.
2026-05-04 00:28:27 +03:00