刘光辉
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.fileStorage;
 
import jnpf.base.ActionResult;
import jnpf.base.ActionResultCode;
import jnpf.exception.DataException;
import jnpf.file.WorkflowFileAccessApi;
import jnpf.model.fileaccess.WorkflowFileGrant;
import jnpf.model.fileaccess.WorkflowFileRequest;
import jnpf.util.JsonUtil;
import jnpf.util.TicketUtil;
import jnpf.util.UserProvider;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.function.Function;
 
/** 流程动作准入与 Office 配置上下文绑定;文件传输使用原有公共能力。 */
@Service
public class WorkflowFileTicketService {
    private static final String PREVIEW_CONFIG = "workflow-file-preview-config-v2";
    private final WorkflowFileAccessApi access;
    private final Function<String, String> reader;
    private final Function<String, String> writer;
 
    @Autowired public WorkflowFileTicketService(WorkflowFileAccessApi access) {
        this(access, TicketUtil::parseTicket, payload -> TicketUtil.createTicket(payload, 300L));
    }
    WorkflowFileTicketService(WorkflowFileAccessApi access, Function<String, String> reader, Function<String, String> writer) {
        this.access = access; this.reader = reader; this.writer = writer;
    }
 
    @Data public static class Ticket {
        private String purpose;
        private WorkflowFileRequest source;
    }
 
    public List<WorkflowFileGrant> authorizeDownloads(List<WorkflowFileRequest> requests) {
        if (requests == null || requests.isEmpty() || requests.size() > 100) throw denied();
        List<WorkflowFileGrant> grants = new ArrayList<>();
        // 全部校验完成之后,调用方才可委托原单文件/打包下载。
        for (WorkflowFileRequest request : requests) grants.add(authorizeCurrent(request, "DOWNLOAD"));
        return grants;
    }
 
    public WorkflowFileGrant authorizePreview(WorkflowFileRequest request) {
        return authorizeCurrent(request, "VIEW");
    }
 
    /** 仅供服务器用已验证的结果签发配置上下文,不签发新的文件读取地址。 */
    public String previewTicket(WorkflowFileGrant grant) {
        Ticket ticket = new Ticket(); ticket.setPurpose(PREVIEW_CONFIG); ticket.setSource(grant.getContext());
        return writer.apply(JsonUtil.getObjectToString(ticket));
    }
 
    public WorkflowFileGrant previewAccess(String token) {
        if (token == null || !token.matches("[A-Za-z0-9_-]+")) throw denied();
        Ticket ticket;
        try { ticket = JsonUtil.getJsonToBean(reader.apply(token), Ticket.class); }
        catch (RuntimeException invalid) { throw denied(); }
        if (ticket == null || !PREVIEW_CONFIG.equals(ticket.getPurpose()) || ticket.getSource() == null) throw denied();
        WorkflowFileRequest source = ticket.getSource();
        // 配置获取仍使用当前登录用户,不在 DS 回源请求中模拟票据属主。
        if (!Objects.equals(UserProvider.getLoginUserId(), source.getOwnerId())
                || !Objects.equals(tenant(), source.getTenantId()) || !"VIEW".equals(source.getAction())) throw denied();
        WorkflowFileGrant grant = authorize(source);
        if (!Objects.equals(source.getRecordId(), grant.getContext().getRecordId())
                || !same(source.getTaskId(), grant.getContext().getTaskId())) throw denied();
        return grant;
    }
 
    private WorkflowFileGrant authorizeCurrent(WorkflowFileRequest request, String action) {
        if (request == null) throw denied();
        String user = UserProvider.getLoginUserId();
        if (user == null || user.isBlank()) throw denied();
        WorkflowFileRequest copy = JsonUtil.getJsonToBean(JsonUtil.getObjectToString(request), WorkflowFileRequest.class);
        copy.setOwnerId(user); copy.setTenantId(tenant()); copy.setAction(action);
        return authorize(copy);
    }
 
    private WorkflowFileGrant authorize(WorkflowFileRequest source) {
        ActionResult<WorkflowFileGrant> response = access.authorize(source);
        if (response == null || !ActionResultCode.SUCCESS.getCode().equals(response.getCode()) || response.getData() == null) throw denied();
        WorkflowFileGrant grant = response.getData();
        WorkflowFileRequest actual = grant.getContext();
        if (actual == null || grant.getFile() == null || !Objects.equals(source.getFileId(), grant.getFile().get("fileId"))
                || !Objects.equals(source.getFileId(), actual.getFileId()) || !Objects.equals(source.getTenantId(), actual.getTenantId())
                || !Objects.equals(source.getOwnerId(), actual.getOwnerId()) || !Objects.equals(source.getModelId(), actual.getModelId())
                || !Objects.equals(source.getField(), actual.getField()) || !Objects.equals(source.getAction(), actual.getAction())
                || !same(source.getParentField(), actual.getParentField()) || !same(source.getRowId(), actual.getRowId())
                || !same(source.getContentToken(), actual.getContentToken())
                || !same(source.getDraftId(), actual.getDraftId()) || !same(source.getLeaseId(), actual.getLeaseId())
                || source.getTaskId() != null && !Objects.equals(source.getTaskId(), actual.getTaskId())) throw denied();
        return grant;
    }
    private static String tenant() {
        String value = UserProvider.getUser().getTenantId();
        return value == null || value.isBlank() ? "0" : value;
    }
    private static boolean same(String a, String b) { return Objects.toString(a, "").equals(Objects.toString(b, "")); }
    private static DataException denied() { return new DataException("文件访问上下文无效、已过期或当前权限不足"); }
}