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
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
package com.union.control.mapper.interceptor;
|
|
2
|
+
|
|
3
|
+
import com.epcc.commons.securityproxy.api.SecurityResult;
|
|
4
|
+
import com.epcc.commons.securityproxy.api.SymmetricalSecurityService;
|
|
5
|
+
import com.epcc.dubbo.result.Result;
|
|
6
|
+
import com.nucc.channel.ark.common.annotation.EnDecryptField;
|
|
7
|
+
import com.nucc.channel.ark.common.redis.RedisCacheService;
|
|
8
|
+
import com.nucc.channel.ark.common.util.ResultUtil;
|
|
9
|
+
import com.nucc.channel.ark.common.util.Constant;
|
|
10
|
+
import com.union.control.utils.security.SymmetricalSecurityUtils;
|
|
11
|
+
import com.union.control.service.sensitive.RedisRevealTokenStore;
|
|
12
|
+
import com.union.control.service.sensitive.SensitiveRevealProcessor;
|
|
13
|
+
import org.apache.ibatis.builder.StaticSqlSource;
|
|
14
|
+
import org.apache.ibatis.executor.Executor;
|
|
15
|
+
import org.apache.ibatis.mapping.MappedStatement;
|
|
16
|
+
import org.apache.ibatis.mapping.SqlCommandType;
|
|
17
|
+
import org.apache.ibatis.plugin.Invocation;
|
|
18
|
+
import org.apache.ibatis.session.Configuration;
|
|
19
|
+
import org.apache.ibatis.session.ResultHandler;
|
|
20
|
+
import org.apache.ibatis.session.RowBounds;
|
|
21
|
+
import org.junit.After;
|
|
22
|
+
import org.junit.Test;
|
|
23
|
+
|
|
24
|
+
import java.lang.reflect.Method;
|
|
25
|
+
import java.nio.charset.StandardCharsets;
|
|
26
|
+
import java.util.ArrayList;
|
|
27
|
+
import java.util.Base64;
|
|
28
|
+
import java.util.Collections;
|
|
29
|
+
import java.util.HashMap;
|
|
30
|
+
import java.util.List;
|
|
31
|
+
|
|
32
|
+
import static org.junit.Assert.assertEquals;
|
|
33
|
+
import static org.junit.Assert.assertNotSame;
|
|
34
|
+
import static org.junit.Assert.assertSame;
|
|
35
|
+
import static org.junit.Assert.assertTrue;
|
|
36
|
+
import static org.mockito.Matchers.any;
|
|
37
|
+
import static org.mockito.Mockito.mock;
|
|
38
|
+
import static org.mockito.Mockito.never;
|
|
39
|
+
import static org.mockito.Mockito.times;
|
|
40
|
+
import static org.mockito.Mockito.verify;
|
|
41
|
+
import static org.mockito.Mockito.when;
|
|
42
|
+
|
|
43
|
+
public class AESInterceptorCompatibilityTest {
|
|
44
|
+
private static final String OLD_QUERY =
|
|
45
|
+
"com.nucc.channel.ark.dao.mapper.announce.AnnounceAddressBookMapper.queryById";
|
|
46
|
+
private static final String ENCRYPT_WRITE =
|
|
47
|
+
"com.nucc.channel.ark.dao.mapper.announce.AnnounceAddressBookRecordMapper.insert";
|
|
48
|
+
private static final String DECRYPT_QUERY =
|
|
49
|
+
"com.nucc.channel.ark.dao.mapper.announce.AnnounceAddressBookRecordMapper.selectPageResult";
|
|
50
|
+
private static final String DEMO_INSERT =
|
|
51
|
+
"com.union.control.mapper.SensitiveDataDemoMapper.insert";
|
|
52
|
+
private static final String DEMO_QUERY =
|
|
53
|
+
"com.union.control.mapper.SensitiveDataDemoMapper.query";
|
|
54
|
+
private static final String ADDRESS_BOOK_DEMO_INSERT =
|
|
55
|
+
"com.union.control.mapper.SensitiveDataDemoMapper.insertAddressBook";
|
|
56
|
+
private static final String ADDRESS_BOOK_DEMO_QUERY =
|
|
57
|
+
"com.union.control.mapper.SensitiveDataDemoMapper.queryAddressBook";
|
|
58
|
+
|
|
59
|
+
@After
|
|
60
|
+
public void resetFlags() {
|
|
61
|
+
Constant.flagMap.clear();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@Test
|
|
65
|
+
public void unlistedStatementOnlyProceedsOnce() throws Throwable {
|
|
66
|
+
Executor executor = mock(Executor.class);
|
|
67
|
+
MappedStatement statement = statement("example.Mapper.unlisted", SqlCommandType.SELECT,
|
|
68
|
+
"select 1");
|
|
69
|
+
when(executor.query(statement, null, RowBounds.DEFAULT, null))
|
|
70
|
+
.thenReturn(Collections.emptyList());
|
|
71
|
+
|
|
72
|
+
Object result = interceptor().intercept(queryInvocation(executor, statement, null));
|
|
73
|
+
|
|
74
|
+
assertEquals(Collections.emptyList(), result);
|
|
75
|
+
verify(executor, times(1)).query(statement, null, RowBounds.DEFAULT, null);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@Test
|
|
79
|
+
public void oldQueryRewritesOnlyTheOldTableAndDecryptsAnnotatedFields() throws Throwable {
|
|
80
|
+
Constant.flagMap.put("interceptorItems", "[]");
|
|
81
|
+
Executor executor = mock(Executor.class);
|
|
82
|
+
MappedStatement statement = statement(OLD_QUERY, SqlCommandType.SELECT,
|
|
83
|
+
"select * from t_m_announce_address_book where id = 1 "
|
|
84
|
+
+ "union all select * from t_m_announce_address_book_new");
|
|
85
|
+
Contact encrypted = new Contact(Base64.getEncoder().encodeToString(
|
|
86
|
+
"enc:13800138000".getBytes(StandardCharsets.UTF_8)));
|
|
87
|
+
when(executor.query(any(MappedStatement.class), any(), any(RowBounds.class),
|
|
88
|
+
any(ResultHandler.class))).thenReturn((List) new ArrayList<>(
|
|
89
|
+
Collections.singletonList(encrypted)));
|
|
90
|
+
|
|
91
|
+
List<?> result = (List<?>) interceptor().intercept(queryInvocation(
|
|
92
|
+
executor, statement, new HashMap<String, Object>()));
|
|
93
|
+
|
|
94
|
+
assertEquals("13800138000", ((Contact) result.get(0)).mobile);
|
|
95
|
+
org.mockito.ArgumentCaptor<MappedStatement> captured =
|
|
96
|
+
org.mockito.ArgumentCaptor.forClass(MappedStatement.class);
|
|
97
|
+
verify(executor).query(captured.capture(), any(), any(RowBounds.class),
|
|
98
|
+
any(ResultHandler.class));
|
|
99
|
+
String sql = captured.getValue().getBoundSql(Collections.emptyMap()).getSql();
|
|
100
|
+
assertTrue(sql.contains("from t_m_announce_address_book_new where"));
|
|
101
|
+
assertEquals(2, occurrences(sql, "t_m_announce_address_book_new"));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@Test
|
|
105
|
+
public void encryptStatementUsesTheProductionAnnotationDispatch() throws Throwable {
|
|
106
|
+
Executor executor = mock(Executor.class);
|
|
107
|
+
MappedStatement statement = statement(ENCRYPT_WRITE, SqlCommandType.INSERT,
|
|
108
|
+
"insert into demo(value) values (?)");
|
|
109
|
+
Contact contact = new Contact("13800138000");
|
|
110
|
+
when(executor.update(any(MappedStatement.class), any())).thenAnswer(call -> {
|
|
111
|
+
assertEquals(Base64.getEncoder().encodeToString(
|
|
112
|
+
"enc:13800138000".getBytes(StandardCharsets.UTF_8)),
|
|
113
|
+
((Contact) call.getArguments()[1]).mobile);
|
|
114
|
+
return 1;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
assertEquals(1, interceptor().intercept(updateInvocation(executor, statement, contact)));
|
|
118
|
+
assertEquals(Base64.getEncoder().encodeToString(
|
|
119
|
+
"enc:13800138000".getBytes(StandardCharsets.UTF_8)), contact.mobile);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@Test
|
|
123
|
+
public void demoStatementsEncryptWritesAndDecryptQueries() throws Throwable {
|
|
124
|
+
Executor executor = mock(Executor.class);
|
|
125
|
+
Contact parameter = new Contact("13800138000");
|
|
126
|
+
MappedStatement insert = statement(DEMO_INSERT, SqlCommandType.INSERT,
|
|
127
|
+
"insert into sensitive_data_demo.sensitive_data_demo(phone_number) values (?)");
|
|
128
|
+
when(executor.update(any(MappedStatement.class), any())).thenReturn(1);
|
|
129
|
+
|
|
130
|
+
interceptor().intercept(updateInvocation(executor, insert, parameter));
|
|
131
|
+
|
|
132
|
+
String ciphertext = Base64.getEncoder().encodeToString(
|
|
133
|
+
"enc:13800138000".getBytes(StandardCharsets.UTF_8));
|
|
134
|
+
assertEquals(ciphertext, parameter.mobile);
|
|
135
|
+
|
|
136
|
+
Contact row = new Contact(ciphertext);
|
|
137
|
+
MappedStatement query = statement(DEMO_QUERY, SqlCommandType.SELECT,
|
|
138
|
+
"select phone_number from sensitive_data_demo.sensitive_data_demo");
|
|
139
|
+
when(executor.query(any(MappedStatement.class), any(), any(RowBounds.class),
|
|
140
|
+
any(ResultHandler.class))).thenReturn((List) Collections.singletonList(row));
|
|
141
|
+
|
|
142
|
+
interceptor().intercept(queryInvocation(executor, query, null));
|
|
143
|
+
|
|
144
|
+
assertEquals("13800138000", row.mobile);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@Test
|
|
148
|
+
public void addressBookDemoRouteStaysNarrowAndRewritesWrites() throws Throwable {
|
|
149
|
+
Executor bypass = mock(Executor.class);
|
|
150
|
+
Contact untouched = new Contact("13800138000");
|
|
151
|
+
MappedStatement unexpected = statement(ADDRESS_BOOK_DEMO_QUERY + "Unexpected",
|
|
152
|
+
SqlCommandType.INSERT, "insert into t_m_announce_address_book values (?)");
|
|
153
|
+
when(bypass.update(unexpected, untouched)).thenReturn(1);
|
|
154
|
+
|
|
155
|
+
interceptor().intercept(updateInvocation(bypass, unexpected, untouched));
|
|
156
|
+
|
|
157
|
+
assertEquals("13800138000", untouched.mobile);
|
|
158
|
+
verify(bypass).update(unexpected, untouched);
|
|
159
|
+
|
|
160
|
+
Executor executor = mock(Executor.class);
|
|
161
|
+
Contact parameter = new Contact("13800138000");
|
|
162
|
+
MappedStatement insert = statement(ADDRESS_BOOK_DEMO_INSERT, SqlCommandType.INSERT,
|
|
163
|
+
"insert into t_m_announce_address_book(mobile_number) values (?)");
|
|
164
|
+
when(executor.update(any(MappedStatement.class), any())).thenReturn(1);
|
|
165
|
+
|
|
166
|
+
interceptor().intercept(updateInvocation(executor, insert, parameter));
|
|
167
|
+
|
|
168
|
+
assertEquals(Base64.getEncoder().encodeToString(
|
|
169
|
+
"enc:13800138000".getBytes(StandardCharsets.UTF_8)), parameter.mobile);
|
|
170
|
+
org.mockito.ArgumentCaptor<MappedStatement> captured =
|
|
171
|
+
org.mockito.ArgumentCaptor.forClass(MappedStatement.class);
|
|
172
|
+
verify(executor).update(captured.capture(), any());
|
|
173
|
+
assertTrue(captured.getValue().getBoundSql(parameter).getSql()
|
|
174
|
+
.contains("t_m_announce_address_book_new"));
|
|
175
|
+
verify(executor, never()).clearLocalCache();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@Test
|
|
179
|
+
public void demoSaveStatementsKeepEncryptionAndAddressBookBypass() throws Throwable {
|
|
180
|
+
for (String method : new String[]{"insertSaved", "update", "updateAddressBook"}) {
|
|
181
|
+
Executor executor = mock(Executor.class);
|
|
182
|
+
Contact parameter = new Contact("13800138000");
|
|
183
|
+
MappedStatement write = statement("com.union.control.mapper.SensitiveDataDemoMapper." + method,
|
|
184
|
+
SqlCommandType.UPDATE, "update t_m_announce_address_book set mobile_number = ?");
|
|
185
|
+
when(executor.update(any(MappedStatement.class), any())).thenReturn(1);
|
|
186
|
+
interceptor().intercept(updateInvocation(executor, write, parameter));
|
|
187
|
+
assertEquals(Base64.getEncoder().encodeToString(
|
|
188
|
+
"enc:13800138000".getBytes(StandardCharsets.UTF_8)), parameter.mobile);
|
|
189
|
+
org.mockito.ArgumentCaptor<MappedStatement> captured =
|
|
190
|
+
org.mockito.ArgumentCaptor.forClass(MappedStatement.class);
|
|
191
|
+
verify(executor).update(captured.capture(), any());
|
|
192
|
+
assertEquals(method.equals("updateAddressBook"),
|
|
193
|
+
captured.getValue().getBoundSql(parameter).getSql().contains("address_book_new"));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@Test
|
|
198
|
+
public void exactSavedRowQueriesStayMaskedInStrictModeIncludingWhitelistedRoles() throws Throwable {
|
|
199
|
+
SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(new PrefixGateway());
|
|
200
|
+
SensitiveRevealProcessor processor = new SensitiveRevealProcessor(crypto,
|
|
201
|
+
new RedisRevealTokenStore(new RecordingStore(), 300));
|
|
202
|
+
AESInterceptor interceptor = interceptor(processor, false, "001");
|
|
203
|
+
org.springframework.test.util.ReflectionTestUtils.setField(interceptor, "strictMode", true);
|
|
204
|
+
for (String method : new String[]{"queryById", "queryAddressBookById"}) {
|
|
205
|
+
Executor executor = mock(Executor.class);
|
|
206
|
+
AddressBookContact row = new AddressBookContact("001", crypto.encryptWithCheck("13800138000"));
|
|
207
|
+
when(executor.query(any(MappedStatement.class), any(), any(RowBounds.class), any(ResultHandler.class)))
|
|
208
|
+
.thenReturn((List) Collections.singletonList(row));
|
|
209
|
+
interceptor.intercept(queryInvocation(executor,
|
|
210
|
+
statement("com.union.control.mapper.SensitiveDataDemoMapper." + method,
|
|
211
|
+
SqlCommandType.SELECT, "select * from t_m_announce_address_book where id = ?"), null));
|
|
212
|
+
assertTrue(row.mobile.startsWith("[#138****8000#VIEW:rt_"));
|
|
213
|
+
org.mockito.ArgumentCaptor<MappedStatement> captured =
|
|
214
|
+
org.mockito.ArgumentCaptor.forClass(MappedStatement.class);
|
|
215
|
+
verify(executor).query(captured.capture(), any(), any(RowBounds.class), any(ResultHandler.class));
|
|
216
|
+
assertEquals(method.equals("queryAddressBookById"),
|
|
217
|
+
captured.getValue().getBoundSql(null).getSql().contains("address_book_new"));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
@Test
|
|
222
|
+
public void pluginOnlyWrapsExecutors() {
|
|
223
|
+
AESInterceptor interceptor = interceptor();
|
|
224
|
+
Object other = new Object();
|
|
225
|
+
Executor executor = mock(Executor.class);
|
|
226
|
+
assertSame(other, interceptor.plugin(other));
|
|
227
|
+
assertNotSame(executor, interceptor.plugin(executor));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
@Test
|
|
231
|
+
public void keepsTheProductionNoArgConstructionPath() throws Exception {
|
|
232
|
+
assertTrue(AESInterceptor.class.newInstance() instanceof AESInterceptor);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@Test
|
|
236
|
+
public void revealQueryDisablesMappedStatementCacheAndClearsExecutorCache() throws Throwable {
|
|
237
|
+
PrefixGateway gateway = new PrefixGateway();
|
|
238
|
+
SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(gateway);
|
|
239
|
+
RecordingStore store = new RecordingStore();
|
|
240
|
+
SensitiveRevealProcessor processor =
|
|
241
|
+
new SensitiveRevealProcessor(crypto, new RedisRevealTokenStore(store, 300));
|
|
242
|
+
AESInterceptor interceptor = interceptor(processor, true, "");
|
|
243
|
+
Executor executor = mock(Executor.class);
|
|
244
|
+
MappedStatement statement = cachedStatement(OLD_QUERY, "select * from demo");
|
|
245
|
+
Contact row = new Contact(crypto.encryptWithCheck("13800138000"));
|
|
246
|
+
when(executor.query(any(MappedStatement.class), any(), any(RowBounds.class),
|
|
247
|
+
any(ResultHandler.class))).thenReturn((List) new ArrayList<>(
|
|
248
|
+
Collections.singletonList(row)));
|
|
249
|
+
|
|
250
|
+
interceptor.intercept(queryInvocation(executor, statement, new HashMap<String, Object>()));
|
|
251
|
+
|
|
252
|
+
assertTrue(row.mobile.matches("\\[#138\\*{4}8000#VIEW:rt_[A-Za-z0-9_-]{43}\\]"));
|
|
253
|
+
assertEquals(1, store.values.size());
|
|
254
|
+
org.mockito.ArgumentCaptor<MappedStatement> captured =
|
|
255
|
+
org.mockito.ArgumentCaptor.forClass(MappedStatement.class);
|
|
256
|
+
verify(executor).query(captured.capture(), any(), any(RowBounds.class),
|
|
257
|
+
any(ResultHandler.class));
|
|
258
|
+
assertEquals(false, captured.getValue().isUseCache());
|
|
259
|
+
verify(executor).clearLocalCache();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
@Test
|
|
263
|
+
public void revealAppliesToEveryGeneralDecryptQuery() throws Throwable {
|
|
264
|
+
PrefixGateway gateway = new PrefixGateway();
|
|
265
|
+
SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(gateway);
|
|
266
|
+
RecordingStore store = new RecordingStore();
|
|
267
|
+
SensitiveRevealProcessor processor =
|
|
268
|
+
new SensitiveRevealProcessor(crypto, new RedisRevealTokenStore(store, 300));
|
|
269
|
+
AESInterceptor interceptor = interceptor(processor, true, "");
|
|
270
|
+
Executor executor = mock(Executor.class);
|
|
271
|
+
MappedStatement statement = cachedStatement(DECRYPT_QUERY, "select * from demo");
|
|
272
|
+
Contact row = new Contact(crypto.encryptWithCheck("13800138000"));
|
|
273
|
+
when(executor.query(any(MappedStatement.class), any(), any(RowBounds.class),
|
|
274
|
+
any(ResultHandler.class))).thenReturn((List) new ArrayList<>(
|
|
275
|
+
Collections.singletonList(row)));
|
|
276
|
+
|
|
277
|
+
interceptor.intercept(queryInvocation(executor, statement, null));
|
|
278
|
+
|
|
279
|
+
assertTrue(row.mobile.matches("\\[#138\\*{4}8000#VIEW:rt_[A-Za-z0-9_-]{43}\\]"));
|
|
280
|
+
assertEquals(1, store.values.size());
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
@Test
|
|
284
|
+
public void disabledRevealReturnsPlaintextForGeneralDecryptQuery() throws Throwable {
|
|
285
|
+
SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(new PrefixGateway());
|
|
286
|
+
Executor executor = mock(Executor.class);
|
|
287
|
+
MappedStatement statement = statement(DECRYPT_QUERY, SqlCommandType.SELECT,
|
|
288
|
+
"select * from demo");
|
|
289
|
+
Contact row = new Contact(crypto.encryptWithCheck("13800138000"));
|
|
290
|
+
when(executor.query(any(MappedStatement.class), any(), any(RowBounds.class),
|
|
291
|
+
any(ResultHandler.class))).thenReturn((List) new ArrayList<>(
|
|
292
|
+
Collections.singletonList(row)));
|
|
293
|
+
|
|
294
|
+
interceptor().intercept(queryInvocation(executor, statement, null));
|
|
295
|
+
|
|
296
|
+
assertEquals("13800138000", row.mobile);
|
|
297
|
+
verify(executor).clearLocalCache();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
@Test
|
|
301
|
+
public void addressBookRevealKeepsConfiguredRolesPlainAndMasksOtherRows() throws Throwable {
|
|
302
|
+
PrefixGateway gateway = new PrefixGateway();
|
|
303
|
+
SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(gateway);
|
|
304
|
+
RecordingStore store = new RecordingStore();
|
|
305
|
+
SensitiveRevealProcessor processor =
|
|
306
|
+
new SensitiveRevealProcessor(crypto, new RedisRevealTokenStore(store, 300));
|
|
307
|
+
AESInterceptor interceptor = interceptor(processor, true, "001,002");
|
|
308
|
+
Executor executor = mock(Executor.class);
|
|
309
|
+
MappedStatement statement = statement(OLD_QUERY, SqlCommandType.SELECT,
|
|
310
|
+
"select * from t_m_announce_address_book");
|
|
311
|
+
AddressBookContact plaintext = new AddressBookContact("001",
|
|
312
|
+
crypto.encryptWithCheck("13800138000"));
|
|
313
|
+
AddressBookContact masked = new AddressBookContact("003",
|
|
314
|
+
crypto.encryptWithCheck("13900139000"));
|
|
315
|
+
List<AddressBookContact> rows = new ArrayList<>();
|
|
316
|
+
rows.add(plaintext);
|
|
317
|
+
rows.add(masked);
|
|
318
|
+
when(executor.query(any(MappedStatement.class), any(), any(RowBounds.class),
|
|
319
|
+
any(ResultHandler.class))).thenReturn((List) rows);
|
|
320
|
+
|
|
321
|
+
interceptor.intercept(queryInvocation(executor, statement, null));
|
|
322
|
+
|
|
323
|
+
assertEquals("13800138000", plaintext.mobile);
|
|
324
|
+
assertTrue(masked.mobile.matches("\\[#139\\*{4}9000#VIEW:rt_[A-Za-z0-9_-]{43}\\]"));
|
|
325
|
+
assertEquals(1, store.values.size());
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
@Test
|
|
329
|
+
public void strictModeMasksEveryAddressBookRoleEvenWhenRevealIsDisabled() throws Throwable {
|
|
330
|
+
SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(new PrefixGateway());
|
|
331
|
+
RecordingStore store = new RecordingStore();
|
|
332
|
+
SensitiveRevealProcessor processor =
|
|
333
|
+
new SensitiveRevealProcessor(crypto, new RedisRevealTokenStore(store, 300));
|
|
334
|
+
AESInterceptor interceptor = interceptor(processor, false, "001,002,003");
|
|
335
|
+
org.springframework.test.util.ReflectionTestUtils.setField(interceptor, "strictMode", true);
|
|
336
|
+
List<AddressBookContact> rows = new ArrayList<>();
|
|
337
|
+
for (String role : new String[]{"001", "002", "003", "unknown", "", null}) {
|
|
338
|
+
rows.add(new AddressBookContact(role, crypto.encryptWithCheck("13800138000")));
|
|
339
|
+
}
|
|
340
|
+
Executor executor = mock(Executor.class);
|
|
341
|
+
when(executor.query(any(MappedStatement.class), any(), any(RowBounds.class),
|
|
342
|
+
any(ResultHandler.class))).thenReturn((List) rows);
|
|
343
|
+
|
|
344
|
+
interceptor.intercept(queryInvocation(executor,
|
|
345
|
+
statement(OLD_QUERY, SqlCommandType.SELECT,
|
|
346
|
+
"select * from t_m_announce_address_book"), null));
|
|
347
|
+
|
|
348
|
+
for (AddressBookContact row : rows) {
|
|
349
|
+
assertTrue(row.mobile.startsWith("[#138****8000#VIEW:rt_"));
|
|
350
|
+
}
|
|
351
|
+
assertEquals(rows.size(), store.values.size());
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
@Test
|
|
355
|
+
public void strictModeMasksGeneralQueriesEvenWhenRevealIsDisabled() throws Throwable {
|
|
356
|
+
SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(new PrefixGateway());
|
|
357
|
+
SensitiveRevealProcessor processor = new SensitiveRevealProcessor(crypto,
|
|
358
|
+
new RedisRevealTokenStore(new RecordingStore(), 300));
|
|
359
|
+
AESInterceptor interceptor = interceptor(processor, false, "001");
|
|
360
|
+
org.springframework.test.util.ReflectionTestUtils.setField(interceptor, "strictMode", true);
|
|
361
|
+
Contact row = new Contact(crypto.encryptWithCheck("13800138000"));
|
|
362
|
+
Executor executor = mock(Executor.class);
|
|
363
|
+
when(executor.query(any(MappedStatement.class), any(), any(RowBounds.class),
|
|
364
|
+
any(ResultHandler.class))).thenReturn((List) Collections.singletonList(row));
|
|
365
|
+
|
|
366
|
+
interceptor.intercept(queryInvocation(executor,
|
|
367
|
+
statement(DECRYPT_QUERY, SqlCommandType.SELECT, "select * from demo"), null));
|
|
368
|
+
|
|
369
|
+
assertTrue(row.mobile.startsWith("[#138****8000#VIEW:rt_"));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
@Test(expected = IllegalArgumentException.class)
|
|
373
|
+
public void invalidAddressBookRoleConfigurationFailsClosed() {
|
|
374
|
+
SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(new PrefixGateway());
|
|
375
|
+
new AddressBookHandler(new SensitiveRevealProcessor(crypto, null), "001,*");
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
@Test
|
|
379
|
+
public void emptyDynamicItemsBypassConfiguredStatements() throws Throwable {
|
|
380
|
+
Constant.flagMap.put("interceptorItems", "[]");
|
|
381
|
+
Executor executor = mock(Executor.class);
|
|
382
|
+
MappedStatement statement = statement(ENCRYPT_WRITE, SqlCommandType.INSERT,
|
|
383
|
+
"insert into demo(value) values (?)");
|
|
384
|
+
Contact contact = new Contact("13800138000");
|
|
385
|
+
when(executor.update(statement, contact)).thenReturn(1);
|
|
386
|
+
|
|
387
|
+
assertEquals(1, interceptor().intercept(updateInvocation(executor, statement, contact)));
|
|
388
|
+
|
|
389
|
+
assertEquals("13800138000", contact.mobile);
|
|
390
|
+
verify(executor).update(statement, contact);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
@Test
|
|
394
|
+
public void nullAddressBookParameterStillRewritesAndDecrypts() throws Throwable {
|
|
395
|
+
Executor executor = mock(Executor.class);
|
|
396
|
+
MappedStatement statement = statement(OLD_QUERY, SqlCommandType.SELECT,
|
|
397
|
+
"select * from t_m_announce_address_book");
|
|
398
|
+
Contact encrypted = new Contact(Base64.getEncoder().encodeToString(
|
|
399
|
+
"enc:13800138000".getBytes(StandardCharsets.UTF_8)));
|
|
400
|
+
List<Contact> rows = new ArrayList<>(Collections.singletonList(encrypted));
|
|
401
|
+
when(executor.query(any(MappedStatement.class), any(), any(RowBounds.class),
|
|
402
|
+
any(ResultHandler.class))).thenReturn((List) rows);
|
|
403
|
+
|
|
404
|
+
Object result = interceptor().intercept(queryInvocation(executor, statement, null));
|
|
405
|
+
|
|
406
|
+
assertSame(rows, result);
|
|
407
|
+
assertEquals("13800138000", encrypted.mobile);
|
|
408
|
+
org.mockito.ArgumentCaptor<MappedStatement> captured =
|
|
409
|
+
org.mockito.ArgumentCaptor.forClass(MappedStatement.class);
|
|
410
|
+
verify(executor).query(captured.capture(), any(), any(RowBounds.class),
|
|
411
|
+
any(ResultHandler.class));
|
|
412
|
+
assertTrue(captured.getValue().getBoundSql(null).getSql()
|
|
413
|
+
.contains("t_m_announce_address_book_new"));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
private static AESInterceptor interceptor() {
|
|
417
|
+
return interceptor(new SensitiveRevealProcessor(
|
|
418
|
+
new SymmetricalSecurityUtils(new PrefixGateway()), null), false, "");
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private static AESInterceptor interceptor(SensitiveRevealProcessor processor,
|
|
422
|
+
boolean revealEnabled, String plaintextRoles) {
|
|
423
|
+
return new AESInterceptor(processor,
|
|
424
|
+
new AddressBookHandler(processor, plaintextRoles), revealEnabled);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
private static MappedStatement statement(String id, SqlCommandType command, String sql) {
|
|
428
|
+
Configuration configuration = new Configuration();
|
|
429
|
+
return new MappedStatement.Builder(configuration, id,
|
|
430
|
+
new StaticSqlSource(configuration, sql), command).build();
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private static MappedStatement cachedStatement(String id, String sql) {
|
|
434
|
+
Configuration configuration = new Configuration();
|
|
435
|
+
return new MappedStatement.Builder(configuration, id,
|
|
436
|
+
new StaticSqlSource(configuration, sql), SqlCommandType.SELECT)
|
|
437
|
+
.useCache(true).build();
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
private static Invocation queryInvocation(Executor executor, MappedStatement statement,
|
|
441
|
+
Object parameter) throws Exception {
|
|
442
|
+
Method method = Executor.class.getMethod("query", MappedStatement.class, Object.class,
|
|
443
|
+
RowBounds.class, ResultHandler.class);
|
|
444
|
+
return new Invocation(executor, method,
|
|
445
|
+
new Object[]{statement, parameter, RowBounds.DEFAULT, null});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
private static Invocation updateInvocation(Executor executor, MappedStatement statement,
|
|
449
|
+
Object parameter) throws Exception {
|
|
450
|
+
Method method = Executor.class.getMethod("update", MappedStatement.class, Object.class);
|
|
451
|
+
return new Invocation(executor, method, new Object[]{statement, parameter});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
private static int occurrences(String value, String needle) {
|
|
455
|
+
return (value.length() - value.replace(needle, "").length()) / needle.length();
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
static class Contact {
|
|
459
|
+
@EnDecryptField
|
|
460
|
+
String mobile;
|
|
461
|
+
|
|
462
|
+
Contact(String mobile) {
|
|
463
|
+
this.mobile = mobile;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
static class AddressBookContact extends Contact {
|
|
468
|
+
String role;
|
|
469
|
+
|
|
470
|
+
AddressBookContact(String role, String mobile) {
|
|
471
|
+
super(mobile);
|
|
472
|
+
this.role = role;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
public String getRole() {
|
|
476
|
+
return role;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
static class PrefixGateway implements SymmetricalSecurityService {
|
|
481
|
+
@Override
|
|
482
|
+
public Result<SecurityResult> encryptByFixedKey(Algorithm algorithm, byte[] plaintext) {
|
|
483
|
+
return Result.success(new SecurityResult(
|
|
484
|
+
("enc:" + new String(plaintext, StandardCharsets.UTF_8))
|
|
485
|
+
.getBytes(StandardCharsets.UTF_8)));
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
@Override
|
|
489
|
+
public Result<SecurityResult> decryptByFixedKey(Algorithm algorithm, byte[] ciphertext) {
|
|
490
|
+
return Result.success(new SecurityResult(
|
|
491
|
+
new String(ciphertext, StandardCharsets.UTF_8)
|
|
492
|
+
.substring(4).getBytes(StandardCharsets.UTF_8)));
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
static class RecordingStore extends RedisCacheService {
|
|
497
|
+
final List<String> values = new ArrayList<>();
|
|
498
|
+
|
|
499
|
+
@Override
|
|
500
|
+
public String setex(String key, int time, String value) {
|
|
501
|
+
values.add(value);
|
|
502
|
+
return ResultUtil.SUCCESS_RESULT;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
package com.union.control.schedule;
|
|
2
|
+
|
|
3
|
+
import org.junit.Test;
|
|
4
|
+
|
|
5
|
+
import static org.assertj.core.api.Assertions.assertThat;
|
|
6
|
+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
7
|
+
|
|
8
|
+
public class ScheduledExecutionTokenTest {
|
|
9
|
+
@Test
|
|
10
|
+
public void issuesAHashableRedactedCapability() {
|
|
11
|
+
ScheduledExecutionToken token = ScheduledExecutionToken.issue();
|
|
12
|
+
|
|
13
|
+
assertThat(token.authorizationHeader()).startsWith("Scheduled ");
|
|
14
|
+
assertThat(token.hash()).hasSize(64).matches("[0-9a-f]{64}");
|
|
15
|
+
assertThat(ScheduledExecutionToken.hashSubmitted(
|
|
16
|
+
token.authorizationHeader().substring("Scheduled ".length())))
|
|
17
|
+
.isEqualTo(token.hash());
|
|
18
|
+
assertThat(token.toString()).doesNotContain(
|
|
19
|
+
token.authorizationHeader().substring("Scheduled ".length()));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@Test
|
|
23
|
+
public void rejectsMalformedSubmittedTokens() {
|
|
24
|
+
assertThatThrownBy(() -> ScheduledExecutionToken.hashSubmitted("invalid"))
|
|
25
|
+
.isInstanceOf(IllegalArgumentException.class);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
package com.union.control.schedule;
|
|
2
|
+
|
|
3
|
+
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
4
|
+
import com.union.control.service.AgentProxyService;
|
|
5
|
+
import com.union.control.service.AgentResponse;
|
|
6
|
+
import com.union.control.service.ScheduledTaskService;
|
|
7
|
+
import org.junit.Test;
|
|
8
|
+
|
|
9
|
+
import java.util.Collections;
|
|
10
|
+
import java.util.concurrent.CountDownLatch;
|
|
11
|
+
import java.util.concurrent.TimeUnit;
|
|
12
|
+
|
|
13
|
+
import static org.mockito.Matchers.anyString;
|
|
14
|
+
import static org.mockito.Matchers.eq;
|
|
15
|
+
import static org.mockito.Mockito.doAnswer;
|
|
16
|
+
import static org.mockito.Mockito.mock;
|
|
17
|
+
import static org.mockito.Mockito.verify;
|
|
18
|
+
import static org.mockito.Mockito.when;
|
|
19
|
+
|
|
20
|
+
public class ScheduledTaskSchedulerTest {
|
|
21
|
+
@Test
|
|
22
|
+
public void persistsSafeAgentFailureDetails() throws Exception {
|
|
23
|
+
ScheduledTaskService service = mock(ScheduledTaskService.class);
|
|
24
|
+
AgentProxyService proxy = mock(AgentProxyService.class);
|
|
25
|
+
CountDownLatch failed = new CountDownLatch(1);
|
|
26
|
+
when(service.pendingRunIds(20)).thenReturn(Collections.singletonList(28L));
|
|
27
|
+
when(service.claimDueRun()).thenReturn(null);
|
|
28
|
+
when(service.beginRun(eq(28L), anyString(), anyString()))
|
|
29
|
+
.thenReturn(true);
|
|
30
|
+
when(proxy.scheduled(anyString())).thenReturn(new AgentResponse(
|
|
31
|
+
502,
|
|
32
|
+
("{\"error\":\"Tool resolve_member_org 重试耗尽:上游查询失败,HTTP 400。\","
|
|
33
|
+
+ "\"code\":\"tool_retry_exhausted\"}")));
|
|
34
|
+
doAnswer(invocation -> {
|
|
35
|
+
failed.countDown();
|
|
36
|
+
return null;
|
|
37
|
+
}).when(service).failRun(28L, "tool_retry_exhausted",
|
|
38
|
+
"Tool resolve_member_org 重试耗尽:上游查询失败,HTTP 400。");
|
|
39
|
+
|
|
40
|
+
ScheduledTaskScheduler scheduler = new ScheduledTaskScheduler(
|
|
41
|
+
service, proxy, new ObjectMapper(), true, 1, 930, 960);
|
|
42
|
+
try {
|
|
43
|
+
scheduler.scan();
|
|
44
|
+
org.junit.Assert.assertTrue(failed.await(2, TimeUnit.SECONDS));
|
|
45
|
+
verify(service).failRun(28L, "tool_retry_exhausted",
|
|
46
|
+
"Tool resolve_member_org 重试耗尽:上游查询失败,HTTP 400。");
|
|
47
|
+
} finally {
|
|
48
|
+
scheduler.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|