union-ark-web 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PROJECT_OVERVIEW.md +405 -0
- package/README.md +177 -0
- package/ark-control/pom.xml +142 -0
- package/ark-control/src/local-mock/java/com/epcc/commons/securityproxy/api/SecurityResult.java +14 -0
- package/ark-control/src/local-mock/java/com/epcc/commons/securityproxy/api/SymmetricalSecurityService.java +11 -0
- package/ark-control/src/local-mock/java/com/epcc/dubbo/result/Result.java +29 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/annotation/EnDecryptField.java +16 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/annotation/EnDecryptFieldLong.java +17 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/annotation/EnDecryptFieldWithTag.java +16 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/exception/BaseDataErrorCode.java +26 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/exception/CheckException.java +33 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/exception/ErrorCode.java +9 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/redis/RedisCacheService.java +131 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/util/Constant.java +24 -0
- package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/util/ResultUtil.java +9 -0
- package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalMockSensitiveProxy.java +40 -0
- package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalRedisCacheService.java +45 -0
- package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalSensitiveHostConfiguration.java +27 -0
- package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalSensitiveProxyConfiguration.java +16 -0
- package/ark-control/src/main/java/com/union/control/ArkControlApplication.java +17 -0
- package/ark-control/src/main/java/com/union/control/dubbo/ProviderAccessLogFilter.java +45 -0
- package/ark-control/src/main/java/com/union/control/mapper/AgentExecutionMapper.java +35 -0
- package/ark-control/src/main/java/com/union/control/mapper/ConversationMapper.java +44 -0
- package/ark-control/src/main/java/com/union/control/mapper/MemoryStoreMapper.java +41 -0
- package/ark-control/src/main/java/com/union/control/mapper/ScheduledTaskMapper.java +96 -0
- package/ark-control/src/main/java/com/union/control/mapper/SensitiveAddressBookDemo.java +31 -0
- package/ark-control/src/main/java/com/union/control/mapper/SensitiveDataDemoMapper.java +24 -0
- package/ark-control/src/main/java/com/union/control/mapper/interceptor/AESInterceptor.java +265 -0
- package/ark-control/src/main/java/com/union/control/mapper/interceptor/AddressBookHandler.java +105 -0
- package/ark-control/src/main/java/com/union/control/schedule/ScheduledExecutionToken.java +55 -0
- package/ark-control/src/main/java/com/union/control/schedule/ScheduledTaskScheduler.java +206 -0
- package/ark-control/src/main/java/com/union/control/service/impl/AgentExecutionServiceImpl.java +212 -0
- package/ark-control/src/main/java/com/union/control/service/impl/AgentProxyServiceImpl.java +143 -0
- package/ark-control/src/main/java/com/union/control/service/impl/ConversationServiceImpl.java +245 -0
- package/ark-control/src/main/java/com/union/control/service/impl/MemoryStoreServiceImpl.java +252 -0
- package/ark-control/src/main/java/com/union/control/service/impl/RunningAnalysisMockServiceImpl.java +301 -0
- package/ark-control/src/main/java/com/union/control/service/impl/ScheduledTaskServiceImpl.java +576 -0
- package/ark-control/src/main/java/com/union/control/service/impl/SensitiveDataDemoServiceImpl.java +144 -0
- package/ark-control/src/main/java/com/union/control/service/sensitive/RedisRevealTokenStore.java +90 -0
- package/ark-control/src/main/java/com/union/control/service/sensitive/SensitiveRevealProcessor.java +319 -0
- package/ark-control/src/main/java/com/union/control/service/sensitive/SensitiveRevealServiceImpl.java +72 -0
- package/ark-control/src/main/java/com/union/control/utils/AgentSupport.java +103 -0
- package/ark-control/src/main/java/com/union/control/utils/security/SymmetricalSecurityUtils.java +160 -0
- package/ark-control/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter +1 -0
- package/ark-control/src/main/resources/application.properties +4 -0
- package/ark-control/src/main/resources/application.yml +32 -0
- package/ark-control/src/main/resources/dubbo-provider.xml +20 -0
- package/ark-control/src/main/resources/mapper/AgentExecutionMapper.xml +75 -0
- package/ark-control/src/main/resources/mapper/ConversationMapper.xml +110 -0
- package/ark-control/src/main/resources/mapper/MemoryStoreMapper.xml +74 -0
- package/ark-control/src/main/resources/mapper/ScheduledTaskMapper.xml +286 -0
- package/ark-control/src/main/resources/mapper/SensitiveDataDemoMapper.xml +55 -0
- package/ark-control/src/main/resources/schema.sql +146 -0
- package/ark-control/src/test/java/com/union/control/AgentExecutionServiceTest.java +170 -0
- package/ark-control/src/test/java/com/union/control/ArkControlStructureContractTest.java +107 -0
- package/ark-control/src/test/java/com/union/control/ConversationServiceTest.java +89 -0
- package/ark-control/src/test/java/com/union/control/MemoryStoreServiceTest.java +26 -0
- package/ark-control/src/test/java/com/union/control/PydanticAiControlContractTest.java +52 -0
- package/ark-control/src/test/java/com/union/control/TestJson.java +45 -0
- package/ark-control/src/test/java/com/union/control/dubbo/ProviderAccessLogFilterTest.java +53 -0
- package/ark-control/src/test/java/com/union/control/local/sensitive/LocalSensitiveProxyConfigurationTest.java +23 -0
- package/ark-control/src/test/java/com/union/control/mapper/interceptor/AESInterceptorCompatibilityTest.java +505 -0
- package/ark-control/src/test/java/com/union/control/schedule/ScheduledExecutionTokenTest.java +27 -0
- package/ark-control/src/test/java/com/union/control/schedule/ScheduledTaskSchedulerTest.java +51 -0
- package/ark-control/src/test/java/com/union/control/service/AgentProxyServiceTest.java +111 -0
- package/ark-control/src/test/java/com/union/control/service/ScheduledTaskServiceTest.java +483 -0
- package/ark-control/src/test/java/com/union/control/service/impl/SensitiveDataDemoServiceImplTest.java +104 -0
- package/ark-control/src/test/java/com/union/control/service/sensitive/RedisRevealTokenStoreTest.java +65 -0
- package/ark-control/src/test/java/com/union/control/service/sensitive/SensitiveRevealBoundaryTest.java +99 -0
- package/ark-control/src/test/java/com/union/control/service/sensitive/SensitiveRevealProcessorCodecTest.java +111 -0
- package/ark-control/src/test/java/com/union/control/service/sensitive/SensitiveRevealProcessorTest.java +111 -0
- package/ark-control/src/test/java/com/union/control/utils/security/SymmetricalSecurityUtilsCompatibilityTest.java +87 -0
- package/ark-control-facade/pom.xml +16 -0
- package/ark-control-facade/src/main/java/com/union/control/service/AgentExecutionService.java +8 -0
- package/ark-control-facade/src/main/java/com/union/control/service/AgentProxyService.java +7 -0
- package/ark-control-facade/src/main/java/com/union/control/service/AgentResponse.java +22 -0
- package/ark-control-facade/src/main/java/com/union/control/service/ConversationService.java +12 -0
- package/ark-control-facade/src/main/java/com/union/control/service/MemoryStoreService.java +12 -0
- package/ark-control-facade/src/main/java/com/union/control/service/RunningAnalysisMockService.java +10 -0
- package/ark-control-facade/src/main/java/com/union/control/service/ScheduledTaskService.java +40 -0
- package/ark-control-facade/src/main/java/com/union/control/service/SensitiveDataDemoService.java +13 -0
- package/ark-control-facade/src/main/java/com/union/control/service/sensitive/SensitiveRevealService.java +18 -0
- package/ark-web/README.md +80 -0
- package/ark-web/pom.xml +105 -0
- package/ark-web/src/local-mock/java/com/epcc/arkweb/mock/ArkAuthServiceMock.java +105 -0
- package/ark-web/src/local-mock/java/com/epcc/arkweb/mock/LocalKaptchaConfiguration.java +26 -0
- package/ark-web/src/main/java/com/epcc/arkweb/Application.java +13 -0
- package/ark-web/src/main/java/com/epcc/arkweb/config/InterceptorConfig.java +20 -0
- package/ark-web/src/main/java/com/epcc/arkweb/config/Realm.java +64 -0
- package/ark-web/src/main/java/com/epcc/arkweb/config/ShiroConfig.java +129 -0
- package/ark-web/src/main/java/com/epcc/arkweb/filter/LoginFormFilter.java +43 -0
- package/ark-web/src/main/java/com/epcc/arkweb/helper/AuthContextHolder.java +39 -0
- package/ark-web/src/main/java/com/epcc/arkweb/helper/AuthenticatedRequest.java +91 -0
- package/ark-web/src/main/java/com/epcc/arkweb/model/ShiroUser.java +21 -0
- package/ark-web/src/main/java/com/epcc/arkweb/utils/ResultMsg.java +19 -0
- package/ark-web/src/main/java/com/epcc/arkweb/vo/llm/ScheduledTaskCommandVO.java +30 -0
- package/ark-web/src/main/java/com/epcc/arkweb/vo/llm/ScheduledTaskQueryVO.java +20 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/CommonController.java +25 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentAuthorizationInterceptor.java +229 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentController.java +165 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentPermission.java +13 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/llm/LlmController.java +209 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/llm/ScheduledTaskController.java +160 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/sensitive/reveal/SensitiveCaptchaService.java +146 -0
- package/ark-web/src/main/java/com/epcc/arkweb/web/sensitive/reveal/SensitiveRevealController.java +163 -0
- package/ark-web/src/main/resources/application.properties +1 -0
- package/ark-web/src/main/resources/application.yml +23 -0
- package/ark-web/src/main/resources/dubbo-consumer.xml +19 -0
- package/ark-web/src/production-overlay/java/com/epcc/arkweb/config/CasShiroConfig.java +229 -0
- package/ark-web/src/production-overlay/java/com/epcc/arkweb/config/InterceptorConfig.java +35 -0
- package/ark-web/src/production-overlay/java/com/epcc/arkweb/config/ShiroConfig.java +161 -0
- package/ark-web/src/test/java/com/epcc/arkweb/ArkWebStructureContractTest.java +290 -0
- package/ark-web/src/test/java/com/epcc/arkweb/ShiroTestSupport.java +32 -0
- package/ark-web/src/test/java/com/epcc/arkweb/config/LocalShiroIntegrationTest.java +121 -0
- package/ark-web/src/test/java/com/epcc/arkweb/helper/AuthenticatedRequestTest.java +131 -0
- package/ark-web/src/test/java/com/epcc/arkweb/local/LocalArkAuthServiceMockTest.java +17 -0
- package/ark-web/src/test/java/com/epcc/arkweb/web/llm/AgentAuthorizationInterceptorTest.java +134 -0
- package/ark-web/src/test/java/com/epcc/arkweb/web/llm/AgentControllerAuthorizationTest.java +97 -0
- package/ark-web/src/test/java/com/epcc/arkweb/web/llm/LlmControllerTest.java +113 -0
- package/ark-web/src/test/java/com/epcc/arkweb/web/sensitive/reveal/SensitiveRevealControllerTest.java +273 -0
- package/package.json +18 -0
- package/pom.xml +16 -0
package/ark-control/src/main/java/com/union/control/service/impl/SensitiveDataDemoServiceImpl.java
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
package com.union.control.service.impl;
|
|
2
|
+
|
|
3
|
+
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
4
|
+
import com.union.control.mapper.SensitiveAddressBookDemo;
|
|
5
|
+
import com.union.control.mapper.SensitiveDataDemoMapper;
|
|
6
|
+
import com.union.control.service.SensitiveDataDemoService;
|
|
7
|
+
import com.union.control.utils.AgentSupport;
|
|
8
|
+
import com.union.control.service.sensitive.SensitiveRevealService;
|
|
9
|
+
import org.springframework.transaction.annotation.Transactional;
|
|
10
|
+
import java.util.regex.Matcher;
|
|
11
|
+
import java.util.regex.Pattern;
|
|
12
|
+
import org.springframework.stereotype.Service;
|
|
13
|
+
|
|
14
|
+
import java.util.ArrayList;
|
|
15
|
+
import java.util.LinkedHashMap;
|
|
16
|
+
import java.util.List;
|
|
17
|
+
import java.util.Map;
|
|
18
|
+
|
|
19
|
+
@Service("sensitiveDataDemoService")
|
|
20
|
+
public class SensitiveDataDemoServiceImpl implements SensitiveDataDemoService {
|
|
21
|
+
private final SensitiveDataDemoMapper mapper;
|
|
22
|
+
private final ObjectMapper json;
|
|
23
|
+
private final SensitiveRevealService reveal;
|
|
24
|
+
private static final Pattern MARKER = Pattern.compile("\\[#([^\\]\\r\\n]*?)#VIEW:(rt_[A-Za-z0-9_-]{43})\\]");
|
|
25
|
+
|
|
26
|
+
public SensitiveDataDemoServiceImpl(SensitiveDataDemoMapper mapper, ObjectMapper json, SensitiveRevealService reveal) {
|
|
27
|
+
this.mapper = mapper;
|
|
28
|
+
this.json = json;
|
|
29
|
+
this.reveal = reveal;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public int insert(String input) {
|
|
33
|
+
Map<String, Object> payload = AgentSupport.request(json, input);
|
|
34
|
+
return mapper.insert(AgentSupport.text(payload, "phoneNumber", 64, true));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public List<Map<String, Object>> query(String input) {
|
|
38
|
+
AgentSupport.request(json, input);
|
|
39
|
+
return mapper.query();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public int insertAddressBook(String input) {
|
|
43
|
+
Map<String, Object> payload = AgentSupport.request(json, input);
|
|
44
|
+
SensitiveAddressBookDemo row = new SensitiveAddressBookDemo();
|
|
45
|
+
row.setName(AgentSupport.text(payload, "name", 64, true));
|
|
46
|
+
row.setRole(AgentSupport.text(payload, "role", 32, true));
|
|
47
|
+
row.setEmail(AgentSupport.text(payload, "email", 255, true));
|
|
48
|
+
row.setTelephone(AgentSupport.text(payload, "telephone", 32, true));
|
|
49
|
+
row.setMobileNumber(AgentSupport.text(payload, "mobileNumber", 32, true));
|
|
50
|
+
return mapper.insertAddressBook(row);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public List<Map<String, Object>> queryAddressBook(String input) {
|
|
54
|
+
AgentSupport.request(json, input);
|
|
55
|
+
List<Map<String, Object>> result = new ArrayList<>();
|
|
56
|
+
for (SensitiveAddressBookDemo row : mapper.queryAddressBook()) {
|
|
57
|
+
result.add(addressBookValue(row));
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@Transactional
|
|
63
|
+
public Map<String, Object> save(String input) {
|
|
64
|
+
Map<String, Object> payload = AgentSupport.request(json, input);
|
|
65
|
+
AgentSupport.userId(payload);
|
|
66
|
+
Long id = id(payload);
|
|
67
|
+
Map<String, Object> row = new LinkedHashMap<>();
|
|
68
|
+
row.put("phoneNumber", editableText(payload, "phoneNumber", 64));
|
|
69
|
+
if (id == null) {
|
|
70
|
+
mapper.insertSaved(row);
|
|
71
|
+
id = AgentSupport.number(row.get("id"));
|
|
72
|
+
} else {
|
|
73
|
+
row.put("id", id);
|
|
74
|
+
if (mapper.update(row) != 1) throw new IllegalArgumentException("记录不存在");
|
|
75
|
+
}
|
|
76
|
+
Map<String, Object> saved = mapper.queryById(id);
|
|
77
|
+
if (saved == null) throw new IllegalStateException("保存结果不存在");
|
|
78
|
+
return saved;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Transactional
|
|
82
|
+
public Map<String, Object> saveAddressBook(String input) {
|
|
83
|
+
Map<String, Object> payload = AgentSupport.request(json, input);
|
|
84
|
+
AgentSupport.userId(payload);
|
|
85
|
+
SensitiveAddressBookDemo row = new SensitiveAddressBookDemo();
|
|
86
|
+
row.setId(id(payload));
|
|
87
|
+
row.setName(AgentSupport.text(payload, "name", 64, true));
|
|
88
|
+
row.setRole(AgentSupport.text(payload, "role", 32, true));
|
|
89
|
+
row.setEmail(editableText(payload, "email", 255));
|
|
90
|
+
row.setTelephone(editableText(payload, "telephone", 32));
|
|
91
|
+
row.setMobileNumber(editableText(payload, "mobileNumber", 32));
|
|
92
|
+
if (row.getId() == null) mapper.insertAddressBook(row);
|
|
93
|
+
else if (mapper.updateAddressBook(row) != 1) throw new IllegalArgumentException("记录不存在");
|
|
94
|
+
SensitiveAddressBookDemo saved = mapper.queryAddressBookById(row.getId());
|
|
95
|
+
if (saved == null) throw new IllegalStateException("保存结果不存在");
|
|
96
|
+
return addressBookValue(saved);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private String editableText(Map<String, Object> payload, String key, int max) {
|
|
100
|
+
Object raw = payload.get(key);
|
|
101
|
+
if (!(raw instanceof String) || ((String) raw).length() > max * 100)
|
|
102
|
+
throw new IllegalArgumentException(key + " 非法");
|
|
103
|
+
String value = (String) raw;
|
|
104
|
+
Matcher matcher = MARKER.matcher(value);
|
|
105
|
+
StringBuffer resolved = new StringBuffer();
|
|
106
|
+
while (matcher.find()) {
|
|
107
|
+
Map<String, Object> tokenInput = new LinkedHashMap<>();
|
|
108
|
+
tokenInput.put("userId", payload.get("userId"));
|
|
109
|
+
tokenInput.put("orgCode", payload.get("orgCode"));
|
|
110
|
+
tokenInput.put("roleId", payload.get("roleId"));
|
|
111
|
+
tokenInput.put("token", matcher.group(2));
|
|
112
|
+
final String encoded;
|
|
113
|
+
try { encoded = json.writeValueAsString(tokenInput); }
|
|
114
|
+
catch (java.io.IOException error) { throw new IllegalStateException("请求参数非法", error); }
|
|
115
|
+
matcher.appendReplacement(resolved, Matcher.quoteReplacement(reveal.reveal(encoded)));
|
|
116
|
+
}
|
|
117
|
+
matcher.appendTail(resolved);
|
|
118
|
+
value = resolved.toString();
|
|
119
|
+
if (value.trim().isEmpty() || value.length() > max || value.contains("[#") || value.contains("#VIEW:"))
|
|
120
|
+
throw new IllegalArgumentException(key + " 非法或脱敏标记已失效,请刷新后重试");
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private static Long id(Map<String, Object> payload) {
|
|
125
|
+
if (payload.get("id") == null) return null;
|
|
126
|
+
Object value = payload.get("id");
|
|
127
|
+
if (!(value instanceof Integer) && !(value instanceof Long)) throw new IllegalArgumentException("id 非法");
|
|
128
|
+
long id = ((Number) value).longValue();
|
|
129
|
+
if (id <= 0) throw new IllegalArgumentException("id 非法");
|
|
130
|
+
return id;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private static Map<String, Object> addressBookValue(SensitiveAddressBookDemo row) {
|
|
134
|
+
Map<String, Object> value = new LinkedHashMap<>();
|
|
135
|
+
value.put("id", row.getId());
|
|
136
|
+
value.put("name", row.getName());
|
|
137
|
+
value.put("role", row.getRole());
|
|
138
|
+
value.put("email", row.getEmail());
|
|
139
|
+
value.put("telephone", row.getTelephone());
|
|
140
|
+
value.put("mobileNumber", row.getMobileNumber());
|
|
141
|
+
value.put("createdAt", row.getCreatedAt());
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
144
|
+
}
|
package/ark-control/src/main/java/com/union/control/service/sensitive/RedisRevealTokenStore.java
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
package com.union.control.service.sensitive;
|
|
2
|
+
|
|
3
|
+
import com.nucc.channel.ark.common.redis.RedisCacheService;
|
|
4
|
+
import com.nucc.channel.ark.common.util.ResultUtil;
|
|
5
|
+
import org.springframework.beans.factory.annotation.Value;
|
|
6
|
+
import org.springframework.stereotype.Component;
|
|
7
|
+
|
|
8
|
+
import java.nio.charset.StandardCharsets;
|
|
9
|
+
import java.security.MessageDigest;
|
|
10
|
+
import java.security.NoSuchAlgorithmException;
|
|
11
|
+
import java.util.List;
|
|
12
|
+
|
|
13
|
+
@Component
|
|
14
|
+
public final class RedisRevealTokenStore {
|
|
15
|
+
private static final String PREFIX = "sensitive:reveal:";
|
|
16
|
+
private final RedisCacheService redis;
|
|
17
|
+
private final int ttlSeconds;
|
|
18
|
+
|
|
19
|
+
public RedisRevealTokenStore(RedisCacheService redis,
|
|
20
|
+
@Value("${sensitive.reveal.ttl-seconds:300}") long ttlSeconds) {
|
|
21
|
+
if (redis == null) throw new IllegalArgumentException("Redis service is required");
|
|
22
|
+
if (ttlSeconds <= 0 || ttlSeconds > Integer.MAX_VALUE) {
|
|
23
|
+
throw new IllegalArgumentException("Reveal TTL is out of range");
|
|
24
|
+
}
|
|
25
|
+
this.redis = redis;
|
|
26
|
+
this.ttlSeconds = (int) ttlSeconds;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
boolean putAll(List<Entry> entries) {
|
|
30
|
+
if (entries.isEmpty()) return true;
|
|
31
|
+
try {
|
|
32
|
+
for (Entry entry : entries) {
|
|
33
|
+
if (!ResultUtil.SUCCESS_RESULT.equals(
|
|
34
|
+
redis.setex(key(entry.token), ttlSeconds, entry.ciphertext))) return false;
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
} catch (RuntimeException error) {
|
|
38
|
+
throw new StoreUnavailableException(error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
String get(String token) {
|
|
43
|
+
try {
|
|
44
|
+
String ciphertext = redis.get(key(token));
|
|
45
|
+
if (ResultUtil.FAIL_RESULT.equals(ciphertext)) {
|
|
46
|
+
throw new StoreUnavailableException();
|
|
47
|
+
}
|
|
48
|
+
if (ciphertext != null && ciphertext.length() > 12000) {
|
|
49
|
+
throw new StoreUnavailableException();
|
|
50
|
+
}
|
|
51
|
+
return ciphertext;
|
|
52
|
+
} catch (StoreUnavailableException error) {
|
|
53
|
+
throw error;
|
|
54
|
+
} catch (RuntimeException error) {
|
|
55
|
+
throw new StoreUnavailableException(error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static String key(String token) {
|
|
60
|
+
try {
|
|
61
|
+
byte[] hash = MessageDigest.getInstance("SHA-256")
|
|
62
|
+
.digest(token.getBytes(StandardCharsets.UTF_8));
|
|
63
|
+
StringBuilder key = new StringBuilder(PREFIX);
|
|
64
|
+
for (byte value : hash) key.append(String.format("%02x", value & 0xff));
|
|
65
|
+
return key.toString();
|
|
66
|
+
} catch (NoSuchAlgorithmException impossible) {
|
|
67
|
+
throw new IllegalStateException(impossible);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static final class Entry {
|
|
72
|
+
final String token;
|
|
73
|
+
final String ciphertext;
|
|
74
|
+
|
|
75
|
+
Entry(String token, String ciphertext) {
|
|
76
|
+
this.token = token;
|
|
77
|
+
this.ciphertext = ciphertext;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public static final class StoreUnavailableException extends RuntimeException {
|
|
82
|
+
StoreUnavailableException() {
|
|
83
|
+
super("Sensitive reveal store unavailable");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
StoreUnavailableException(Throwable cause) {
|
|
87
|
+
super("Sensitive reveal store unavailable", cause);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
package/ark-control/src/main/java/com/union/control/service/sensitive/SensitiveRevealProcessor.java
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
package com.union.control.service.sensitive;
|
|
2
|
+
|
|
3
|
+
import com.nucc.channel.ark.common.annotation.EnDecryptField;
|
|
4
|
+
import com.nucc.channel.ark.common.annotation.EnDecryptFieldLong;
|
|
5
|
+
import com.nucc.channel.ark.common.annotation.EnDecryptFieldWithTag;
|
|
6
|
+
import com.nucc.channel.ark.common.exception.CheckException;
|
|
7
|
+
import com.union.control.utils.security.SymmetricalSecurityUtils;
|
|
8
|
+
import org.slf4j.Logger;
|
|
9
|
+
import org.slf4j.LoggerFactory;
|
|
10
|
+
import org.springframework.beans.factory.annotation.Autowired;
|
|
11
|
+
import org.springframework.stereotype.Component;
|
|
12
|
+
|
|
13
|
+
import java.lang.reflect.Array;
|
|
14
|
+
import java.lang.reflect.Field;
|
|
15
|
+
import java.lang.reflect.Modifier;
|
|
16
|
+
import java.security.SecureRandom;
|
|
17
|
+
import java.util.ArrayList;
|
|
18
|
+
import java.util.Arrays;
|
|
19
|
+
import java.util.Base64;
|
|
20
|
+
import java.util.Collections;
|
|
21
|
+
import java.util.HashSet;
|
|
22
|
+
import java.util.IdentityHashMap;
|
|
23
|
+
import java.util.List;
|
|
24
|
+
import java.util.Map;
|
|
25
|
+
import java.util.Set;
|
|
26
|
+
import java.util.function.Consumer;
|
|
27
|
+
import java.util.regex.Matcher;
|
|
28
|
+
import java.util.regex.Pattern;
|
|
29
|
+
|
|
30
|
+
import static com.nucc.channel.ark.common.util.Constant.REGEX_EMAIL;
|
|
31
|
+
import static com.nucc.channel.ark.common.util.Constant.REGEX_MOBILE;
|
|
32
|
+
import static com.nucc.channel.ark.common.util.Constant.REGEX_TELEPHONE;
|
|
33
|
+
|
|
34
|
+
@SuppressWarnings({"rawtypes", "unchecked"})
|
|
35
|
+
@Component
|
|
36
|
+
public final class SensitiveRevealProcessor {
|
|
37
|
+
private static final Logger LOG = LoggerFactory.getLogger(SensitiveRevealProcessor.class);
|
|
38
|
+
private static final Pattern SENSITIVE = Pattern.compile(
|
|
39
|
+
"(" + REGEX_MOBILE + ")|(" + REGEX_EMAIL + ")|(" + REGEX_TELEPHONE + ")");
|
|
40
|
+
private static final Set<String> MAP_KEYS = new HashSet<>(Arrays.asList(
|
|
41
|
+
"mobileNumber", "phoneNumber", "telNumber", "email"));
|
|
42
|
+
|
|
43
|
+
private final SymmetricalSecurityUtils crypto;
|
|
44
|
+
private final RedisRevealTokenStore tokens;
|
|
45
|
+
private final SecureRandom random;
|
|
46
|
+
|
|
47
|
+
@Autowired
|
|
48
|
+
public SensitiveRevealProcessor(SymmetricalSecurityUtils crypto, RedisRevealTokenStore tokens) {
|
|
49
|
+
this(crypto, tokens, new SecureRandom());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public void encrypt(Object value) throws CheckException {
|
|
53
|
+
transform(value, true, null, visited());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public void decrypt(Object value) throws CheckException {
|
|
57
|
+
transform(value, false, null, visited());
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
SensitiveRevealProcessor(SymmetricalSecurityUtils crypto, RedisRevealTokenStore tokens,
|
|
61
|
+
SecureRandom random) {
|
|
62
|
+
this.crypto = crypto;
|
|
63
|
+
this.tokens = tokens;
|
|
64
|
+
this.random = random;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public void process(Object result) {
|
|
68
|
+
List<ValueTarget> targets = new ArrayList<>();
|
|
69
|
+
try {
|
|
70
|
+
transform(result, false, targets, visited());
|
|
71
|
+
} catch (CheckException impossible) {
|
|
72
|
+
throw new IllegalStateException(impossible);
|
|
73
|
+
}
|
|
74
|
+
apply(targets);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private void apply(List<ValueTarget> targets) {
|
|
78
|
+
List<RedisRevealTokenStore.Entry> entries = new ArrayList<>();
|
|
79
|
+
List<Plan> plans = new ArrayList<>(targets.size());
|
|
80
|
+
for (ValueTarget target : targets) plans.add(plan(target, entries));
|
|
81
|
+
|
|
82
|
+
boolean clickable = entries.isEmpty();
|
|
83
|
+
if (!entries.isEmpty()) {
|
|
84
|
+
try {
|
|
85
|
+
clickable = tokens.putAll(entries);
|
|
86
|
+
} catch (RuntimeException error) {
|
|
87
|
+
clickable = false;
|
|
88
|
+
LOG.warn("Sensitive reveal token batch unavailable match_count={}", entries.size());
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
for (Plan plan : plans) plan.target.set(clickable ? plan.clickable : plan.masked);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private Plan plan(ValueTarget target, List<RedisRevealTokenStore.Entry> entries) {
|
|
95
|
+
if (target.value == null || target.value.isEmpty()) return new Plan(target, target.value, target.value);
|
|
96
|
+
Matcher matcher = SENSITIVE.matcher(target.value);
|
|
97
|
+
StringBuilder masked = new StringBuilder();
|
|
98
|
+
StringBuilder clickable = new StringBuilder();
|
|
99
|
+
int end = 0;
|
|
100
|
+
while (matcher.find()) {
|
|
101
|
+
masked.append(target.value, end, matcher.start());
|
|
102
|
+
clickable.append(target.value, end, matcher.start());
|
|
103
|
+
String plaintext = matcher.group();
|
|
104
|
+
String mask = mask(plaintext);
|
|
105
|
+
masked.append("[#").append(mask).append(']');
|
|
106
|
+
String revealToken = null;
|
|
107
|
+
try {
|
|
108
|
+
String token = token();
|
|
109
|
+
entries.add(new RedisRevealTokenStore.Entry(
|
|
110
|
+
token, crypto.encryptWithCheck(plaintext)));
|
|
111
|
+
revealToken = token;
|
|
112
|
+
} catch (CheckException error) {
|
|
113
|
+
LOG.warn("Sensitive reveal fragment encryption failed error_type={}",
|
|
114
|
+
error.getClass().getSimpleName());
|
|
115
|
+
}
|
|
116
|
+
clickable.append("[#").append(mask);
|
|
117
|
+
if (revealToken != null) clickable.append("#VIEW:").append(revealToken);
|
|
118
|
+
clickable.append(']');
|
|
119
|
+
end = matcher.end();
|
|
120
|
+
}
|
|
121
|
+
if (end == 0) return new Plan(target, target.value, target.value);
|
|
122
|
+
masked.append(target.value, end, target.value.length());
|
|
123
|
+
clickable.append(target.value, end, target.value.length());
|
|
124
|
+
return new Plan(target, masked.toString(), clickable.toString());
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private void addMapValue(final Map map, final Object key, String ciphertext,
|
|
128
|
+
List<ValueTarget> targets) {
|
|
129
|
+
try {
|
|
130
|
+
targets.add(new ValueTarget(crypto.decryptWithCheckNoLog(ciphertext),
|
|
131
|
+
value -> map.put(key, value)));
|
|
132
|
+
} catch (CheckException error) {
|
|
133
|
+
map.put(key, "****");
|
|
134
|
+
LOG.warn("Sensitive reveal field decryption failed field_kind=map error_type={}",
|
|
135
|
+
error.getClass().getSimpleName());
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private void addFieldValue(final Object owner, final Field field, List<ValueTarget> targets) {
|
|
140
|
+
validateField(field);
|
|
141
|
+
try {
|
|
142
|
+
String ciphertext = readField(owner, field);
|
|
143
|
+
final String plaintext = transformFieldValue(field, ciphertext, false);
|
|
144
|
+
targets.add(new ValueTarget(plaintext, value -> writeField(owner, field, value)));
|
|
145
|
+
} catch (CheckException | RuntimeException error) {
|
|
146
|
+
writeField(owner, field, "****");
|
|
147
|
+
LOG.warn("Sensitive reveal field decryption failed field_kind=pojo error_type={}",
|
|
148
|
+
error.getClass().getSimpleName());
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private void transform(Object value, boolean encrypt, List<ValueTarget> revealTargets,
|
|
153
|
+
Set<Object> visited) throws CheckException {
|
|
154
|
+
if (value == null || isSimple(value.getClass()) || !visited.add(value)) return;
|
|
155
|
+
if (value instanceof Map<?, ?>) {
|
|
156
|
+
Map map = (Map) value;
|
|
157
|
+
IdentityHashMap<Object, Object> replacements = revealTargets == null
|
|
158
|
+
? new IdentityHashMap<>() : null;
|
|
159
|
+
for (Object item : map.entrySet()) {
|
|
160
|
+
Map.Entry entry = (Map.Entry) item;
|
|
161
|
+
Object child = entry.getValue();
|
|
162
|
+
if (entry.getKey() instanceof String && MAP_KEYS.contains(entry.getKey())
|
|
163
|
+
&& child instanceof String) {
|
|
164
|
+
if (revealTargets == null) {
|
|
165
|
+
replacements.put(child, encrypt ? crypto.encryptWithCheck((String) child)
|
|
166
|
+
: crypto.decryptWithCheckNoLog((String) child));
|
|
167
|
+
} else {
|
|
168
|
+
addMapValue(map, entry.getKey(), (String) child, revealTargets);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
for (Object item : map.entrySet()) {
|
|
173
|
+
Map.Entry entry = (Map.Entry) item;
|
|
174
|
+
Object child = entry.getValue();
|
|
175
|
+
if (replacements != null && replacements.containsKey(child)) {
|
|
176
|
+
entry.setValue(replacements.get(child));
|
|
177
|
+
} else if (!(entry.getKey() instanceof String && MAP_KEYS.contains(entry.getKey())
|
|
178
|
+
&& child instanceof String)) {
|
|
179
|
+
transform(child, encrypt, revealTargets, visited);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (value instanceof Iterable<?>) {
|
|
185
|
+
for (Object child : (Iterable<?>) value) {
|
|
186
|
+
transform(child, encrypt, revealTargets, visited);
|
|
187
|
+
}
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (value.getClass().isArray()) {
|
|
191
|
+
for (int i = 0; i < Array.getLength(value); i++) {
|
|
192
|
+
transform(Array.get(value, i), encrypt, revealTargets, visited);
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
for (Class<?> type = value.getClass(); type != null && type != Object.class;
|
|
197
|
+
type = type.getSuperclass()) {
|
|
198
|
+
for (Field field : type.getDeclaredFields()) {
|
|
199
|
+
if (!isSensitive(field)) continue;
|
|
200
|
+
if (revealTargets == null) transformField(value, field, encrypt);
|
|
201
|
+
else addFieldValue(value, field, revealTargets);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private void transformField(Object owner, Field field, boolean encrypt) throws CheckException {
|
|
207
|
+
validateField(field);
|
|
208
|
+
writeField(owner, field, transformFieldValue(field, readField(owner, field), encrypt));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private static void validateField(Field field) {
|
|
212
|
+
if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())
|
|
213
|
+
|| field.getType() != String.class) {
|
|
214
|
+
throw new IllegalStateException("Sensitive field must be a writable String: "
|
|
215
|
+
+ field.getDeclaringClass().getName() + "." + field.getName());
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private static String readField(Object owner, Field field) {
|
|
220
|
+
boolean accessible = field.isAccessible();
|
|
221
|
+
try {
|
|
222
|
+
field.setAccessible(true);
|
|
223
|
+
return (String) field.get(owner);
|
|
224
|
+
} catch (IllegalAccessException error) {
|
|
225
|
+
throw new IllegalStateException("Sensitive field is not accessible", error);
|
|
226
|
+
} finally {
|
|
227
|
+
field.setAccessible(accessible);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private static void writeField(Object owner, Field field, String value) {
|
|
232
|
+
boolean accessible = field.isAccessible();
|
|
233
|
+
try {
|
|
234
|
+
field.setAccessible(true);
|
|
235
|
+
field.set(owner, value);
|
|
236
|
+
} catch (IllegalAccessException error) {
|
|
237
|
+
throw new IllegalStateException("Sensitive field is not accessible", error);
|
|
238
|
+
} finally {
|
|
239
|
+
field.setAccessible(accessible);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private String transformFieldValue(Field field, String value, boolean encrypt)
|
|
244
|
+
throws CheckException {
|
|
245
|
+
if (value == null) return null;
|
|
246
|
+
if (field.isAnnotationPresent(EnDecryptField.class)) {
|
|
247
|
+
return encrypt ? crypto.encryptWithCheck(value) : crypto.decryptWithCheckNoLog(value);
|
|
248
|
+
}
|
|
249
|
+
if (field.isAnnotationPresent(EnDecryptFieldWithTag.class)) {
|
|
250
|
+
return encrypt ? crypto.encryptWithTag(value) : crypto.decryptWithTag(value);
|
|
251
|
+
}
|
|
252
|
+
EnDecryptFieldLong longField = field.getAnnotation(EnDecryptFieldLong.class);
|
|
253
|
+
return encrypt ? crypto.encryptLongString(value, longField.chunkSize())
|
|
254
|
+
: crypto.decryptLongString(value);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private static boolean isSensitive(Field field) {
|
|
258
|
+
return field.isAnnotationPresent(EnDecryptField.class)
|
|
259
|
+
|| field.isAnnotationPresent(EnDecryptFieldLong.class)
|
|
260
|
+
|| field.isAnnotationPresent(EnDecryptFieldWithTag.class);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private static boolean isSimple(Class<?> type) {
|
|
264
|
+
return type.isPrimitive() || CharSequence.class.isAssignableFrom(type)
|
|
265
|
+
|| Number.class.isAssignableFrom(type) || Boolean.class == type
|
|
266
|
+
|| Character.class == type || Enum.class.isAssignableFrom(type);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private static Set<Object> visited() {
|
|
270
|
+
return Collections.newSetFromMap(new IdentityHashMap<Object, Boolean>());
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private String token() {
|
|
274
|
+
byte[] bytes = new byte[32];
|
|
275
|
+
random.nextBytes(bytes);
|
|
276
|
+
return "rt_" + Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
private static String mask(String value) {
|
|
280
|
+
int at = value.indexOf('@');
|
|
281
|
+
if (at > 0) {
|
|
282
|
+
return value.substring(0, 1) + "***" + value.substring(at);
|
|
283
|
+
}
|
|
284
|
+
String digits = value.replace("-", "");
|
|
285
|
+
if (digits.length() == 11 && digits.charAt(0) == '1') {
|
|
286
|
+
return digits.substring(0, 3) + "****" + digits.substring(7);
|
|
287
|
+
}
|
|
288
|
+
if (digits.length() > 7) {
|
|
289
|
+
return digits.substring(0, 3) + "****" + digits.substring(digits.length() - 4);
|
|
290
|
+
}
|
|
291
|
+
return "****";
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private static final class ValueTarget {
|
|
295
|
+
final String value;
|
|
296
|
+
final Consumer<String> setter;
|
|
297
|
+
|
|
298
|
+
ValueTarget(String value, Consumer<String> setter) {
|
|
299
|
+
this.value = value;
|
|
300
|
+
this.setter = setter;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
void set(String value) {
|
|
304
|
+
setter.accept(value);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private static final class Plan {
|
|
309
|
+
final ValueTarget target;
|
|
310
|
+
final String masked;
|
|
311
|
+
final String clickable;
|
|
312
|
+
|
|
313
|
+
Plan(ValueTarget target, String masked, String clickable) {
|
|
314
|
+
this.target = target;
|
|
315
|
+
this.masked = masked;
|
|
316
|
+
this.clickable = clickable;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
package com.union.control.service.sensitive;
|
|
2
|
+
|
|
3
|
+
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
4
|
+
import com.nucc.channel.ark.common.exception.CheckException;
|
|
5
|
+
import com.union.control.utils.security.SymmetricalSecurityUtils;
|
|
6
|
+
import com.union.control.utils.AgentSupport;
|
|
7
|
+
import com.union.control.service.sensitive.SensitiveRevealService.ExpiredTokenException;
|
|
8
|
+
import com.union.control.service.sensitive.SensitiveRevealService.InvalidTokenException;
|
|
9
|
+
import com.union.control.service.sensitive.SensitiveRevealService.RevealDecryptionException;
|
|
10
|
+
import com.union.control.service.sensitive.SensitiveRevealService.StoreUnavailableException;
|
|
11
|
+
import org.slf4j.Logger;
|
|
12
|
+
import org.slf4j.LoggerFactory;
|
|
13
|
+
import org.springframework.stereotype.Service;
|
|
14
|
+
|
|
15
|
+
import java.util.Map;
|
|
16
|
+
import java.util.regex.Pattern;
|
|
17
|
+
import java.util.UUID;
|
|
18
|
+
|
|
19
|
+
@Service("sensitiveRevealService")
|
|
20
|
+
public final class SensitiveRevealServiceImpl implements SensitiveRevealService {
|
|
21
|
+
private static final Logger LOG = LoggerFactory.getLogger(SensitiveRevealServiceImpl.class);
|
|
22
|
+
private static final Pattern TOKEN = Pattern.compile("rt_[A-Za-z0-9_-]{43}");
|
|
23
|
+
private final RedisRevealTokenStore tokens;
|
|
24
|
+
private final SymmetricalSecurityUtils crypto;
|
|
25
|
+
private final ObjectMapper json;
|
|
26
|
+
|
|
27
|
+
public SensitiveRevealServiceImpl(RedisRevealTokenStore tokens, SymmetricalSecurityUtils crypto,
|
|
28
|
+
ObjectMapper json) {
|
|
29
|
+
this.tokens = tokens;
|
|
30
|
+
this.crypto = crypto;
|
|
31
|
+
this.json = json;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public String reveal(String input) {
|
|
35
|
+
long started = System.nanoTime();
|
|
36
|
+
Map<String, Object> request = AgentSupport.request(json, input);
|
|
37
|
+
Object rawRequestId = request.get("revealRequestId");
|
|
38
|
+
String requestId = rawRequestId instanceof String
|
|
39
|
+
&& ((String) rawRequestId).matches("[A-Za-z0-9-]{1,64}")
|
|
40
|
+
? (String) rawRequestId : UUID.randomUUID().toString();
|
|
41
|
+
LOG.info("Sensitive reveal Control received request_id={}", requestId);
|
|
42
|
+
Object rawToken = request.get("token");
|
|
43
|
+
String token = rawToken instanceof String ? (String) rawToken : null;
|
|
44
|
+
if (token == null || !TOKEN.matcher(token).matches()) {
|
|
45
|
+
LOG.info("Sensitive reveal Control rejected request_id={} reason=invalid_token", requestId);
|
|
46
|
+
throw new InvalidTokenException();
|
|
47
|
+
}
|
|
48
|
+
String ciphertext;
|
|
49
|
+
try {
|
|
50
|
+
ciphertext = tokens.get(token);
|
|
51
|
+
} catch (RedisRevealTokenStore.StoreUnavailableException error) {
|
|
52
|
+
LOG.info("Sensitive reveal Control failed request_id={} stage=token_lookup reason=store_unavailable", requestId);
|
|
53
|
+
throw new StoreUnavailableException();
|
|
54
|
+
}
|
|
55
|
+
if (ciphertext == null) {
|
|
56
|
+
LOG.info("Sensitive reveal Control rejected request_id={} reason=token_expired", requestId);
|
|
57
|
+
throw new ExpiredTokenException();
|
|
58
|
+
}
|
|
59
|
+
LOG.info("Sensitive reveal Control token resolved request_id={}", requestId);
|
|
60
|
+
try {
|
|
61
|
+
String plaintext = crypto.decryptWithCheckNoLog(ciphertext);
|
|
62
|
+
LOG.info("Sensitive reveal Control completed request_id={} elapsed_ms={}",
|
|
63
|
+
requestId, (System.nanoTime() - started) / 1000000);
|
|
64
|
+
return plaintext;
|
|
65
|
+
} catch (CheckException | RuntimeException error) {
|
|
66
|
+
LOG.info("Sensitive reveal Control failed request_id={} stage=decrypt error_type={}",
|
|
67
|
+
requestId, error.getClass().getSimpleName());
|
|
68
|
+
throw new RevealDecryptionException();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
}
|