package com.xfestudio.xfeservermanager.api.policy;
import com.xfestudio.xfeservermanager.api.command.CommandSourceKind;
import com.xfestudio.xfeservermanager.api.util.ApiChecks;
import java.util.Set;
import java.util.UUID;
/** Subject predicates are combined with logical AND; values inside a set use OR. */
public record SubjectMatcher(
Set<UUID> actorIds,
Set<String> roles,
Integer minimumOpLevel,
Integer maximumOpLevel,
Set<CommandSourceKind> sources,
Boolean onlineMode
) {
public SubjectMatcher {
actorIds = ApiChecks.immutableSet(actorIds, "actorIds");
roles = ApiChecks.immutableLowercaseSet(roles, "roles");
sources = ApiChecks.immutableSet(sources, "sources");
validateOpLevel(minimumOpLevel, "minimumOpLevel");
validateOpLevel(maximumOpLevel, "maximumOpLevel");
if (minimumOpLevel != null && maximumOpLevel != null && minimumOpLevel > maximumOpLevel) {
throw new IllegalArgumentException("minimumOpLevel must not exceed maximumOpLevel");
}
}
public static SubjectMatcher any() {
return new SubjectMatcher(Set.of(), Set.of(), null, null, Set.of(), null);
}
public boolean matches(ActorContext actor) {
if (!actorIds.isEmpty() && (actor.actorId() == null || !actorIds.contains(actor.actorId()))) {
return false;
}
if (!roles.isEmpty() && roles.stream().noneMatch(actor.roles()::contains)) {
return false;
}
if (minimumOpLevel != null && actor.opLevel() < minimumOpLevel) {
return false;
}
if (maximumOpLevel != null && actor.opLevel() > maximumOpLevel) {
return false;
}
if (!sources.isEmpty() && !sources.contains(actor.source())) {
return false;
}
return onlineMode == null || onlineMode == actor.onlineMode();
}
public int specificity() {
int result = 0;
result += actorIds.isEmpty() ? 0 : 10_000 - Math.min(actorIds.size(), 9_999);
result += roles.isEmpty() ? 0 : 1_000 - Math.min(roles.size(), 999);
result += minimumOpLevel == null ? 0 : 40;
result += maximumOpLevel == null ? 0 : 40;
result += sources.isEmpty() ? 0 : 20 - Math.min(sources.size(), 19);
result += onlineMode == null ? 0 : 1;
return result;
}
public int matchingRoleWeight(ActorContext actor) {
return roles.stream().filter(actor.roles()::contains).mapToInt(actor::weightOf).max().orElse(0);
}
private static void validateOpLevel(Integer value, String name) {
if (value != null && (value < 0 || value > 4)) {
throw new IllegalArgumentException(name + " must be between 0 and 4");
}
}
}
package com.xfestudio.xfeservermanager.api.policy;
import com.xfestudio.xfeservermanager.api.command.CommandSourceKind;
import com.xfestudio.xfeservermanager.api.util.ApiChecks;
import java.util.Set;
import java.util.UUID;
/** Subject predicates are combined with logical AND; values inside a set use OR. */
public record SubjectMatcher(
Set<UUID> actorIds,
Set<String> roles,
Integer minimumOpLevel,
Integer maximumOpLevel,
Set<CommandSourceKind> sources,
Boolean onlineMode
) {
public SubjectMatcher {
actorIds = ApiChecks.immutableSet(actorIds, "actorIds");
roles = ApiChecks.immutableLowercaseSet(roles, "roles");
sources = ApiChecks.immutableSet(sources, "sources");
validateOpLevel(minimumOpLevel, "minimumOpLevel");
validateOpLevel(maximumOpLevel, "maximumOpLevel");
if (minimumOpLevel != null && maximumOpLevel != null && minimumOpLevel > maximumOpLevel) {
throw new IllegalArgumentException("minimumOpLevel must not exceed maximumOpLevel");
}
}
public static SubjectMatcher any() {
return new SubjectMatcher(Set.of(), Set.of(), null, null, Set.of(), null);
}
public boolean matches(ActorContext actor) {
if (!actorIds.isEmpty() && (actor.actorId() == null || !actorIds.contains(actor.actorId()))) {
return false;
}
if (!roles.isEmpty() && roles.stream().noneMatch(actor.roles()::contains)) {
return false;
}
if (minimumOpLevel != null && actor.opLevel() < minimumOpLevel) {
return false;
}
if (maximumOpLevel != null && actor.opLevel() > maximumOpLevel) {
return false;
}
if (!sources.isEmpty() && !sources.contains(actor.source())) {
return false;
}
return onlineMode == null || onlineMode == actor.onlineMode();
}
public int specificity() {
int result = 0;
result += actorIds.isEmpty() ? 0 : 10_000 - Math.min(actorIds.size(), 9_999);
result += roles.isEmpty() ? 0 : 1_000 - Math.min(roles.size(), 999);
result += minimumOpLevel == null ? 0 : 40;
result += maximumOpLevel == null ? 0 : 40;
result += sources.isEmpty() ? 0 : 20 - Math.min(sources.size(), 19);
result += onlineMode == null ? 0 : 1;
return result;
}
public int matchingRoleWeight(ActorContext actor) {
return roles.stream().filter(actor.roles()::contains).mapToInt(actor::weightOf).max().orElse(0);
}
private static void validateOpLevel(Integer value, String name) {
if (value != null && (value < 0 || value > 4)) {
throw new IllegalArgumentException(name + " must be between 0 and 4");
}
}
}