刘光辉
4 小时以前 06f314545ac3514d7ce28939920866ae8188d31c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
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); }
}