feat: implement ClaimMappingService with equals/contains/regex matching
- Evaluates JWT claims against mapping rules - Supports equals, contains (list + space-separated), regex match types - Results sorted by priority - 7 unit tests covering all match types and edge cases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
package com.cameleer3.server.core.rbac;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ClaimMappingService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ClaimMappingService.class);
|
||||
|
||||
public record MappingResult(ClaimMappingRule rule) {}
|
||||
|
||||
public List<MappingResult> evaluate(List<ClaimMappingRule> rules, Map<String, Object> claims) {
|
||||
return rules.stream()
|
||||
.sorted(Comparator.comparingInt(ClaimMappingRule::priority))
|
||||
.filter(rule -> matches(rule, claims))
|
||||
.map(MappingResult::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private boolean matches(ClaimMappingRule rule, Map<String, Object> claims) {
|
||||
Object claimValue = claims.get(rule.claim());
|
||||
if (claimValue == null) return false;
|
||||
|
||||
return switch (rule.matchType()) {
|
||||
case "equals" -> equalsMatch(claimValue, rule.matchValue());
|
||||
case "contains" -> containsMatch(claimValue, rule.matchValue());
|
||||
case "regex" -> regexMatch(claimValue, rule.matchValue());
|
||||
default -> {
|
||||
log.warn("Unknown match type: {}", rule.matchType());
|
||||
yield false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean equalsMatch(Object claimValue, String matchValue) {
|
||||
if (claimValue instanceof String s) {
|
||||
return s.equalsIgnoreCase(matchValue);
|
||||
}
|
||||
return String.valueOf(claimValue).equalsIgnoreCase(matchValue);
|
||||
}
|
||||
|
||||
private boolean containsMatch(Object claimValue, String matchValue) {
|
||||
if (claimValue instanceof List<?> list) {
|
||||
return list.stream().anyMatch(item -> String.valueOf(item).equalsIgnoreCase(matchValue));
|
||||
}
|
||||
if (claimValue instanceof String s) {
|
||||
// Space-separated string (e.g., OAuth2 scope claim)
|
||||
return Arrays.stream(s.split("\\s+"))
|
||||
.anyMatch(part -> part.equalsIgnoreCase(matchValue));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean regexMatch(Object claimValue, String matchValue) {
|
||||
String s = String.valueOf(claimValue);
|
||||
try {
|
||||
return Pattern.matches(matchValue, s);
|
||||
} catch (Exception e) {
|
||||
log.warn("Invalid regex in claim mapping rule: {}", matchValue, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user