刘光辉
5 小时以前 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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package jnpf.elnService.template;
 
import jnpf.database.util.TenantDataSourceUtil;
import jnpf.elnEntity.ElnDocumentFileVo;
import jnpf.elnEntity.template.ElnTemplateVersionEntity;
import jnpf.elnMapper.ElnTemplateMapper;
import jnpf.util.JsonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
 
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
 
@Service
public class TemplateAnnotationAuthorizer {
 
    private static final String DRAFT = "DRAFT";
    private static final String ENABLED = "enabled";
 
    private final TemplateAnnotationTicketService ticketService;
    private final TemplateLookup templateLookup;
    private final AnnotationAccess annotationAccess;
    private final TenantRoute tenantRoute;
 
    @Autowired
    public TemplateAnnotationAuthorizer(TemplateAnnotationTicketService ticketService,
                                        ElnTemplateMapper templateMapper,
                                        ElnTemplateAnnotationAccessService annotationAccessService) {
        this(ticketService, templateMapper::selectById, annotationAccessService::canAnnotate,
                new TenantRoute() {
            @Override
            public void switchTo(String tenantId) {
                TenantDataSourceUtil.switchTenant(tenantId);
            }
 
            @Override
            public void clear() {
                TenantDataSourceUtil.clearLocalTenantInfo();
            }
        });
    }
 
    TemplateAnnotationAuthorizer(TemplateAnnotationTicketService ticketService,
                                 TemplateLookup templateLookup,
                                 AnnotationAccess annotationAccess,
                                 TenantRoute tenantRoute) {
        this.ticketService = ticketService;
        this.templateLookup = templateLookup;
        this.annotationAccess = annotationAccess;
        this.tenantRoute = tenantRoute;
    }
 
    public TemplateAnnotationAuthorization authorize(String ticket, String templateId) {
        return authorize(ticket, templateId, false);
    }
 
    public TemplateAnnotationAuthorization authorize(String ticket) {
        return authorize(ticket, null, true);
    }
 
    private TemplateAnnotationAuthorization authorize(String ticket, String templateId,
                                                       boolean useTicketTemplateId) {
        boolean authorized = false;
        try {
            TemplateAnnotationTicketService.AnnotationTicketContext context = ticketService.parse(ticket);
            String expectedTemplateId = useTicketTemplateId ? context.getTemplateId() : templateId;
            if (!Objects.equals(context.getTemplateId(), expectedTemplateId)) {
                throw unauthorized();
            }
            if (!annotationAccess.canAnnotate(context.getTenantId(), context.getUserId(),
                    context.getTemplateId(), context.getFileId())) {
                throw forbidden();
            }
            switchTenant(context.getTenantId());
 
            ElnTemplateVersionEntity template = findTemplate(expectedTemplateId);
            if (template == null) {
                throw conflict();
            }
            if (!Objects.equals(context.getTenantId(), template.getFTenantId())
                    || !Objects.equals(context.getTemplateId(), template.getId())) {
                throw unauthorized();
            }
            String fileId = uniqueDocxFileId(template.getMubanFile());
            if (!Objects.equals(context.getFileId(), fileId)) {
                throw unauthorized();
            }
            if ((template.getFDeleteMark() != null && template.getFDeleteMark() != 0)
                    || !DRAFT.equals(template.getBizStatus()) || !ENABLED.equals(template.getBizEnabled())) {
                throw conflict();
            }
            TemplateAnnotationAuthorization authorization = new TemplateAnnotationAuthorization(
                    context.getTenantId(), context.getUserId(), template.getId(), fileId);
            authorized = true;
            return authorization;
        } finally {
            if (!authorized) {
                tenantRoute.clear();
            }
        }
    }
 
    private void switchTenant(String tenantId) {
        try {
            tenantRoute.switchTo(tenantId);
        } catch (RuntimeException e) {
            throw unauthorized();
        }
    }
 
    private ElnTemplateVersionEntity findTemplate(String templateId) {
        try {
            return templateLookup.findById(templateId);
        } catch (RuntimeException e) {
            throw conflict();
        }
    }
 
    private String uniqueDocxFileId(String attachments) {
        try {
            List<ElnDocumentFileVo> files = JsonUtil.getJsonToList(attachments, ElnDocumentFileVo.class);
            if (files == null) {
                throw conflict();
            }
            List<String> docxFileIds = files.stream()
                    .filter(Objects::nonNull)
                    .filter(file -> "docx".equalsIgnoreCase(file.getFileExtension()))
                    .map(ElnDocumentFileVo::getFileId)
                    .filter(StringUtils::hasText)
                    .collect(Collectors.toList());
            if (docxFileIds.size() != 1) {
                throw conflict();
            }
            return docxFileIds.get(0);
        } catch (AnnotationAuthorizationException e) {
            throw e;
        } catch (RuntimeException e) {
            throw conflict();
        }
    }
 
    private AnnotationAuthorizationException unauthorized() {
        return new AnnotationAuthorizationException(401, "标注 Ticket 无效");
    }
 
    private AnnotationAuthorizationException forbidden() {
        return new AnnotationAuthorizationException(403, "当前请求无模板标注权限");
    }
 
    private AnnotationAuthorizationException conflict() {
        return new AnnotationAuthorizationException(409, "模板当前不可标注");
    }
 
    interface TemplateLookup {
        ElnTemplateVersionEntity findById(String templateId);
    }
 
    interface AnnotationAccess {
        boolean canAnnotate(String tenantId, String userId, String templateId, String fileId);
    }
 
    interface TenantRoute {
        void switchTo(String tenantId);
 
        void clear();
    }
 
    public static class AnnotationAuthorizationException extends RuntimeException {
        private static final long serialVersionUID = 1L;
        private final int status;
 
        private AnnotationAuthorizationException(int status, String message) {
            super(message);
            this.status = status;
        }
 
        public int getStatus() {
            return status;
        }
    }
}