package jnpf.flowable.model.filepermission;
|
|
import java.util.*;
|
|
/**
|
* 流程版本内的文件规则契约。此类不读取登录态、不分配全局角色,也不替代原生准入。
|
* 配置引用必须在发布时与该版本的表单、字段及节点校验。
|
*/
|
public final class FilePermissionPolicy {
|
private FilePermissionPolicy() { }
|
|
public enum Action { VIEW, DOWNLOAD, EDIT, REVIEW, ADD, REPLACE, REMOVE }
|
public enum Effect { INHERIT, ALLOW, DENY }
|
public enum Subject { CREATOR, CURRENT_HANDLERS, NON_CURRENT_HANDLERS, PARTICIPANTS, NODE_PARTICIPANTS }
|
public enum Trigger { ACTIVE, ENTERED, COMPLETED, CANCELLED, TERMINATED }
|
public enum OnReturn { KEEP, RELEASE }
|
|
public static final class Rule {
|
public final String id;
|
public final String formId;
|
/** 沿用原生字段标识;子表为 parentField-field,行 ID 属于运行态资源。 */
|
public final String field;
|
public final Subject subject;
|
public final String subjectNodeId;
|
public final Trigger trigger;
|
public final String triggerNodeId;
|
public final OnReturn onReturn;
|
public final Map<Action, Effect> actions;
|
|
private Rule(Map<?, ?> source) {
|
id = text(source, "id");
|
formId = text(source, "formId");
|
field = text(source, "field");
|
subject = enumeration(Subject.class, source.get("subject"));
|
trigger = enumeration(Trigger.class, source.get("trigger"));
|
subjectNodeId = subject == Subject.NODE_PARTICIPANTS ? text(source, "subjectNodeId") : null;
|
triggerNodeId = trigger == Trigger.ACTIVE || trigger == Trigger.ENTERED ? text(source, "triggerNodeId") : null;
|
// 持续规则必须显式选择退回行为,不能默认恢复旧权限快照。
|
onReturn = trigger == Trigger.ENTERED ? enumeration(OnReturn.class, source.get("onReturn")) : null;
|
actions = parseActions(source.get("actions"));
|
if (actions.isEmpty()) throw invalid("规则没有配置文件动作");
|
}
|
}
|
|
/** null 代表旧流程未启用;存在但格式错误不能退回旧授权。 */
|
public static List<Rule> parse(Object config) {
|
if (config == null) return Collections.emptyList();
|
if (!(config instanceof Map)) throw invalid("文件权限配置必须为对象");
|
Map<?, ?> map = (Map<?, ?>) config;
|
if (!(map.get("version") instanceof Number) || ((Number) map.get("version")).doubleValue() != 1) {
|
throw invalid("不支持的文件权限配置版本");
|
}
|
if (!(map.get("rules") instanceof List)) throw invalid("文件规则必须为数组");
|
List<Rule> result = new ArrayList<>();
|
Set<String> ids = new HashSet<>();
|
for (Object value : (List<?>) map.get("rules")) {
|
if (!(value instanceof Map)) throw invalid("文件规则必须为对象");
|
Rule rule = new Rule((Map<?, ?>) value);
|
if (!ids.add(rule.id)) throw invalid("文件规则 ID 重复");
|
result.add(rule);
|
}
|
return Collections.unmodifiableList(result);
|
}
|
|
public static Map<Action, Effect> parseActions(Object value) {
|
if (!(value instanceof Map)) throw invalid("文件动作必须为对象");
|
Map<Action, Effect> result = new EnumMap<>(Action.class);
|
for (Map.Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {
|
result.put(enumeration(Action.class, entry.getKey()), enumeration(Effect.class, entry.getValue()));
|
}
|
return Collections.unmodifiableMap(result);
|
}
|
|
/** availableFields 必须来自该版本的真实 uploadFile 字段,不使用客户端候选列表。 */
|
public static void validateReferences(List<Rule> rules, Map<String, Set<String>> availableFields, Set<String> nodeIds) {
|
Objects.requireNonNull(availableFields, "availableFields");
|
Objects.requireNonNull(nodeIds, "nodeIds");
|
for (Rule rule : rules) {
|
if (!availableFields.getOrDefault(rule.formId, Collections.emptySet()).contains(rule.field)) {
|
throw invalid("文件规则引用的附件字段不存在: " + rule.id);
|
}
|
if (rule.triggerNodeId != null && !nodeIds.contains(rule.triggerNodeId)
|
|| rule.subjectNodeId != null && !nodeIds.contains(rule.subjectNodeId)) {
|
throw invalid("文件规则引用的节点不存在: " + rule.id);
|
}
|
}
|
}
|
|
private static String text(Map<?, ?> map, String key) {
|
Object value = map.get(key);
|
if (!(value instanceof String) || ((String) value).trim().isEmpty()) throw invalid("缺少规则属性: " + key);
|
return (String) value;
|
}
|
|
private static <T extends Enum<T>> T enumeration(Class<T> type, Object value) {
|
if (!(value instanceof String)) throw invalid("无效的 " + type.getSimpleName());
|
try { return Enum.valueOf(type, (String) value); }
|
catch (IllegalArgumentException e) { throw invalid("无效的 " + type.getSimpleName() + ": " + value); }
|
}
|
|
private static IllegalArgumentException invalid(String message) { return new IllegalArgumentException(message); }
|
}
|