package jnpf.flowable.util.filepermission; import jnpf.flowable.model.filepermission.FilePermissionPolicy.Action; import jnpf.flowable.model.filepermission.FilePermissionPolicy.Effect; import jnpf.flowable.model.filepermission.FilePermissionPolicy.Rule; import java.util.*; /** * 纯规则裁决。调用方必须先按真实实例、执行轮次、人员关系解析适用规则。 * 不可直接将请求中的规则、版本、人员或布尔权限传入作为可信授权。 */ public final class FilePermissionEvaluator { private FilePermissionEvaluator() { } public static final class Result { public final long version; public final Map actions; /** 原因保留到规则 ID;不返回其他参与人的身份。 */ public final Map reasons; private Result(long version, Map actions, Map reasons) { this.version = version; this.actions = Collections.unmodifiableMap(actions); this.reasons = Collections.unmodifiableMap(reasons); } } public static Result evaluate(String formId, String field, long version, boolean transitioning, Map nativeUpperBound, Map inherited, Collection applicableRules) { Objects.requireNonNull(formId, "formId"); Objects.requireNonNull(field, "field"); Objects.requireNonNull(nativeUpperBound, "nativeUpperBound"); Objects.requireNonNull(inherited, "inherited"); Objects.requireNonNull(applicableRules, "applicableRules"); if (version < 0) throw new IllegalArgumentException("权限版本无效"); Map actions = new EnumMap<>(Action.class); Map reasons = new EnumMap<>(Action.class); for (Action action : Action.values()) { boolean allow = Boolean.TRUE.equals(inherited.get(action)); String deniedBy = null; for (Rule rule : applicableRules) { if (!formId.equals(rule.formId) || !field.equals(rule.field)) continue; Effect effect = rule.actions.get(action); if (effect == Effect.DENY) { // 顺序不影响裁决和返回原因,便于缓存/审计一致性。 if (deniedBy == null || rule.id.compareTo(deniedBy) < 0) deniedBy = rule.id; } else if (effect == Effect.ALLOW) allow = true; } String reason = transitioning ? "TRANSITIONING" : !Boolean.TRUE.equals(nativeUpperBound.get(action)) ? "NATIVE_PERMISSION" : deniedBy != null ? "RULE:" + deniedBy : !allow ? "INHERITED_DENY" : null; actions.put(action, reason == null); if (reason != null) reasons.put(action, reason); } return new Result(version, actions, reasons); } }