feat(auth): поддержка colon-separated ролей (Альтум-style)

Альтум выдаёт роли формата "ordinis:client:public" / "ordinis:client:internal" /
"ordinis:client:restricted" в realm_access.roles. Старая версия ScopeContext
обрабатывала только "-" / "_" разделители + ORDINIS- префикс — colon-роли
приводили к fallback на PUBLIC.

Теперь normalizeRoleToScope:
- strips "ORDINIS:" (как и "ORDINIS-" / "ORDINIS_")
- берёт сегмент после последнего из "-", "_", ":"

Совместимо с прежними форматами. + 2 unit-теста (всего 13 в ScopeContextTest).
This commit is contained in:
Zimin A.N.
2026-05-04 17:40:43 +03:00
parent 405ddb561b
commit 578fe8f476
2 changed files with 26 additions and 4 deletions
@@ -101,14 +101,18 @@ public class ScopeContext {
* Маппинг имени роли в scope. Поддерживает:
* "ordinis-public" / "public" → PUBLIC
* "ORDINIS_INTERNAL" → INTERNAL
* суффикс после "-" или "_": "x-RESTRICTED" → RESTRICTED
* "ordinis:client:public" → PUBLIC (Альтум-style colon-separated)
* суффикс после "-", "_" или ":": "x:RESTRICTED" → RESTRICTED
* просто "PUBLIC"|"INTERNAL"|"RESTRICTED"
*/
private static String normalizeRoleToScope(String role) {
if (role == null) return null;
String s = role.trim().toUpperCase().replace("ORDINIS-", "").replace("ORDINIS_", "");
int dash = Math.max(s.lastIndexOf('-'), s.lastIndexOf('_'));
if (dash >= 0) s = s.substring(dash + 1);
String s = role.trim().toUpperCase()
.replace("ORDINIS-", "")
.replace("ORDINIS_", "")
.replace("ORDINIS:", "");
int sep = Math.max(Math.max(s.lastIndexOf('-'), s.lastIndexOf('_')), s.lastIndexOf(':'));
if (sep >= 0) s = s.substring(sep + 1);
return s;
}
@@ -58,6 +58,24 @@ class ScopeContextTest {
.containsExactlyInAnyOrder(DataScope.PUBLIC, DataScope.INTERNAL);
}
@Test
void mapsAltumColonSeparatedRoles() {
// Альтум-style: realm role "ordinis:client:public" → DataScope.PUBLIC.
setJwtWithRealmRoles(List.of(
"ordinis:client:public",
"ordinis:client:internal",
"ordinis:client:restricted"));
assertThat(new ScopeContext(propsNoQuery).currentScopes())
.containsExactlyInAnyOrder(DataScope.PUBLIC, DataScope.INTERNAL, DataScope.RESTRICTED);
}
@Test
void mapsMixedColonAndDashRoles() {
setJwtWithRealmRoles(List.of("ordinis:client:public", "ordinis-restricted"));
assertThat(new ScopeContext(propsNoQuery).currentScopes())
.containsExactlyInAnyOrder(DataScope.PUBLIC, DataScope.RESTRICTED);
}
@Test
void unrecognizedRolesGivePublicFallback() {
setJwtWithRealmRoles(List.of("admin", "viewer"));