fix: apply defaultRoles fallback when no claim mapping rules match
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m19s
CI / docker (push) Successful in 1m3s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 36s

When no claim mapping rules are configured or none match the JWT
claims, fall back to assigning the OidcConfig.defaultRoles (e.g.
VIEWER). This restores the behavior that was lost when syncOidcRoles
was replaced with claim mapping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-08 11:33:24 +02:00
parent 9af0043915
commit 529e2c727c

View File

@@ -156,7 +156,7 @@ public class OidcAuthController {
userId, provider, oidcUser.email(), oidcUser.name(), Instant.now()));
// Apply claim mapping rules to assign managed roles/groups from JWT claims
applyClaimMappings(userId, oidcUser.allClaims());
applyClaimMappings(userId, oidcUser.allClaims(), config.get());
List<String> roles = rbacService.getSystemRoleNames(userId);
@@ -180,37 +180,50 @@ public class OidcAuthController {
}
}
private void applyClaimMappings(String userId, Map<String, Object> claims) {
private void applyClaimMappings(String userId, Map<String, Object> claims, OidcConfig oidcConfig) {
List<ClaimMappingRule> rules = claimMappingRepository.findAll();
if (rules.isEmpty()) {
log.debug("No claim mapping rules configured, skipping for user {}", userId);
return;
}
rbacService.clearManagedAssignments(userId);
List<ClaimMappingService.MappingResult> results = claimMappingService.evaluate(rules, claims);
for (var result : results) {
ClaimMappingRule rule = result.rule();
switch (rule.action()) {
case "assignRole" -> {
UUID roleId = SystemRole.BY_NAME.get(SystemRole.normalizeScope(rule.target()));
if (roleId == null) {
log.warn("Claim mapping target role '{}' not found, skipping", rule.target());
continue;
List<ClaimMappingService.MappingResult> results = List.of();
if (!rules.isEmpty()) {
results = claimMappingService.evaluate(rules, claims);
for (var result : results) {
ClaimMappingRule rule = result.rule();
switch (rule.action()) {
case "assignRole" -> {
UUID roleId = SystemRole.BY_NAME.get(SystemRole.normalizeScope(rule.target()));
if (roleId == null) {
log.warn("Claim mapping target role '{}' not found, skipping", rule.target());
continue;
}
rbacService.assignManagedRole(userId, roleId, rule.id());
log.debug("Managed role {} assigned to {} via mapping {}", rule.target(), userId, rule.id());
}
case "addToGroup" -> {
var groups = groupRepository.findAll();
var group = groups.stream().filter(g -> g.name().equalsIgnoreCase(rule.target())).findFirst();
if (group.isEmpty()) {
log.warn("Claim mapping target group '{}' not found, skipping", rule.target());
continue;
}
rbacService.addUserToManagedGroup(userId, group.get().id(), rule.id());
log.debug("Managed group {} assigned to {} via mapping {}", rule.target(), userId, rule.id());
}
rbacService.assignManagedRole(userId, roleId, rule.id());
log.debug("Managed role {} assigned to {} via mapping {}", rule.target(), userId, rule.id());
}
case "addToGroup" -> {
var groups = groupRepository.findAll();
var group = groups.stream().filter(g -> g.name().equalsIgnoreCase(rule.target())).findFirst();
if (group.isEmpty()) {
log.warn("Claim mapping target group '{}' not found, skipping", rule.target());
continue;
}
}
// Fallback: if no mapping rules matched, assign defaultRoles from OIDC config
if (results.isEmpty()) {
List<String> defaultRoles = oidcConfig.defaultRoles();
if (defaultRoles != null && !defaultRoles.isEmpty()) {
for (String roleName : defaultRoles) {
UUID roleId = SystemRole.BY_NAME.get(SystemRole.normalizeScope(roleName));
if (roleId != null) {
rbacService.assignRoleToUser(userId, roleId);
log.debug("Default role {} assigned to {} (no claim mapping matched)", roleName, userId);
}
rbacService.addUserToManagedGroup(userId, group.get().id(), rule.id());
log.debug("Managed group {} assigned to {} via mapping {}", rule.target(), userId, rule.id());
}
}
}