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.
Files changed (122) hide show
  1. package/PROJECT_OVERVIEW.md +405 -0
  2. package/README.md +177 -0
  3. package/ark-control/pom.xml +142 -0
  4. package/ark-control/src/local-mock/java/com/epcc/commons/securityproxy/api/SecurityResult.java +14 -0
  5. package/ark-control/src/local-mock/java/com/epcc/commons/securityproxy/api/SymmetricalSecurityService.java +11 -0
  6. package/ark-control/src/local-mock/java/com/epcc/dubbo/result/Result.java +29 -0
  7. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/annotation/EnDecryptField.java +16 -0
  8. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/annotation/EnDecryptFieldLong.java +17 -0
  9. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/annotation/EnDecryptFieldWithTag.java +16 -0
  10. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/exception/BaseDataErrorCode.java +26 -0
  11. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/exception/CheckException.java +33 -0
  12. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/exception/ErrorCode.java +9 -0
  13. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/redis/RedisCacheService.java +131 -0
  14. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/util/Constant.java +24 -0
  15. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/util/ResultUtil.java +9 -0
  16. package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalMockSensitiveProxy.java +40 -0
  17. package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalRedisCacheService.java +45 -0
  18. package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalSensitiveHostConfiguration.java +27 -0
  19. package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalSensitiveProxyConfiguration.java +16 -0
  20. package/ark-control/src/main/java/com/union/control/ArkControlApplication.java +17 -0
  21. package/ark-control/src/main/java/com/union/control/dubbo/ProviderAccessLogFilter.java +45 -0
  22. package/ark-control/src/main/java/com/union/control/mapper/AgentExecutionMapper.java +35 -0
  23. package/ark-control/src/main/java/com/union/control/mapper/ConversationMapper.java +44 -0
  24. package/ark-control/src/main/java/com/union/control/mapper/MemoryStoreMapper.java +41 -0
  25. package/ark-control/src/main/java/com/union/control/mapper/ScheduledTaskMapper.java +96 -0
  26. package/ark-control/src/main/java/com/union/control/mapper/SensitiveAddressBookDemo.java +31 -0
  27. package/ark-control/src/main/java/com/union/control/mapper/SensitiveDataDemoMapper.java +24 -0
  28. package/ark-control/src/main/java/com/union/control/mapper/interceptor/AESInterceptor.java +265 -0
  29. package/ark-control/src/main/java/com/union/control/mapper/interceptor/AddressBookHandler.java +105 -0
  30. package/ark-control/src/main/java/com/union/control/schedule/ScheduledExecutionToken.java +55 -0
  31. package/ark-control/src/main/java/com/union/control/schedule/ScheduledTaskScheduler.java +206 -0
  32. package/ark-control/src/main/java/com/union/control/service/impl/AgentExecutionServiceImpl.java +212 -0
  33. package/ark-control/src/main/java/com/union/control/service/impl/AgentProxyServiceImpl.java +143 -0
  34. package/ark-control/src/main/java/com/union/control/service/impl/ConversationServiceImpl.java +245 -0
  35. package/ark-control/src/main/java/com/union/control/service/impl/MemoryStoreServiceImpl.java +252 -0
  36. package/ark-control/src/main/java/com/union/control/service/impl/RunningAnalysisMockServiceImpl.java +301 -0
  37. package/ark-control/src/main/java/com/union/control/service/impl/ScheduledTaskServiceImpl.java +576 -0
  38. package/ark-control/src/main/java/com/union/control/service/impl/SensitiveDataDemoServiceImpl.java +144 -0
  39. package/ark-control/src/main/java/com/union/control/service/sensitive/RedisRevealTokenStore.java +90 -0
  40. package/ark-control/src/main/java/com/union/control/service/sensitive/SensitiveRevealProcessor.java +319 -0
  41. package/ark-control/src/main/java/com/union/control/service/sensitive/SensitiveRevealServiceImpl.java +72 -0
  42. package/ark-control/src/main/java/com/union/control/utils/AgentSupport.java +103 -0
  43. package/ark-control/src/main/java/com/union/control/utils/security/SymmetricalSecurityUtils.java +160 -0
  44. package/ark-control/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter +1 -0
  45. package/ark-control/src/main/resources/application.properties +4 -0
  46. package/ark-control/src/main/resources/application.yml +32 -0
  47. package/ark-control/src/main/resources/dubbo-provider.xml +20 -0
  48. package/ark-control/src/main/resources/mapper/AgentExecutionMapper.xml +75 -0
  49. package/ark-control/src/main/resources/mapper/ConversationMapper.xml +110 -0
  50. package/ark-control/src/main/resources/mapper/MemoryStoreMapper.xml +74 -0
  51. package/ark-control/src/main/resources/mapper/ScheduledTaskMapper.xml +286 -0
  52. package/ark-control/src/main/resources/mapper/SensitiveDataDemoMapper.xml +55 -0
  53. package/ark-control/src/main/resources/schema.sql +146 -0
  54. package/ark-control/src/test/java/com/union/control/AgentExecutionServiceTest.java +170 -0
  55. package/ark-control/src/test/java/com/union/control/ArkControlStructureContractTest.java +107 -0
  56. package/ark-control/src/test/java/com/union/control/ConversationServiceTest.java +89 -0
  57. package/ark-control/src/test/java/com/union/control/MemoryStoreServiceTest.java +26 -0
  58. package/ark-control/src/test/java/com/union/control/PydanticAiControlContractTest.java +52 -0
  59. package/ark-control/src/test/java/com/union/control/TestJson.java +45 -0
  60. package/ark-control/src/test/java/com/union/control/dubbo/ProviderAccessLogFilterTest.java +53 -0
  61. package/ark-control/src/test/java/com/union/control/local/sensitive/LocalSensitiveProxyConfigurationTest.java +23 -0
  62. package/ark-control/src/test/java/com/union/control/mapper/interceptor/AESInterceptorCompatibilityTest.java +505 -0
  63. package/ark-control/src/test/java/com/union/control/schedule/ScheduledExecutionTokenTest.java +27 -0
  64. package/ark-control/src/test/java/com/union/control/schedule/ScheduledTaskSchedulerTest.java +51 -0
  65. package/ark-control/src/test/java/com/union/control/service/AgentProxyServiceTest.java +111 -0
  66. package/ark-control/src/test/java/com/union/control/service/ScheduledTaskServiceTest.java +483 -0
  67. package/ark-control/src/test/java/com/union/control/service/impl/SensitiveDataDemoServiceImplTest.java +104 -0
  68. package/ark-control/src/test/java/com/union/control/service/sensitive/RedisRevealTokenStoreTest.java +65 -0
  69. package/ark-control/src/test/java/com/union/control/service/sensitive/SensitiveRevealBoundaryTest.java +99 -0
  70. package/ark-control/src/test/java/com/union/control/service/sensitive/SensitiveRevealProcessorCodecTest.java +111 -0
  71. package/ark-control/src/test/java/com/union/control/service/sensitive/SensitiveRevealProcessorTest.java +111 -0
  72. package/ark-control/src/test/java/com/union/control/utils/security/SymmetricalSecurityUtilsCompatibilityTest.java +87 -0
  73. package/ark-control-facade/pom.xml +16 -0
  74. package/ark-control-facade/src/main/java/com/union/control/service/AgentExecutionService.java +8 -0
  75. package/ark-control-facade/src/main/java/com/union/control/service/AgentProxyService.java +7 -0
  76. package/ark-control-facade/src/main/java/com/union/control/service/AgentResponse.java +22 -0
  77. package/ark-control-facade/src/main/java/com/union/control/service/ConversationService.java +12 -0
  78. package/ark-control-facade/src/main/java/com/union/control/service/MemoryStoreService.java +12 -0
  79. package/ark-control-facade/src/main/java/com/union/control/service/RunningAnalysisMockService.java +10 -0
  80. package/ark-control-facade/src/main/java/com/union/control/service/ScheduledTaskService.java +40 -0
  81. package/ark-control-facade/src/main/java/com/union/control/service/SensitiveDataDemoService.java +13 -0
  82. package/ark-control-facade/src/main/java/com/union/control/service/sensitive/SensitiveRevealService.java +18 -0
  83. package/ark-web/README.md +80 -0
  84. package/ark-web/pom.xml +105 -0
  85. package/ark-web/src/local-mock/java/com/epcc/arkweb/mock/ArkAuthServiceMock.java +105 -0
  86. package/ark-web/src/local-mock/java/com/epcc/arkweb/mock/LocalKaptchaConfiguration.java +26 -0
  87. package/ark-web/src/main/java/com/epcc/arkweb/Application.java +13 -0
  88. package/ark-web/src/main/java/com/epcc/arkweb/config/InterceptorConfig.java +20 -0
  89. package/ark-web/src/main/java/com/epcc/arkweb/config/Realm.java +64 -0
  90. package/ark-web/src/main/java/com/epcc/arkweb/config/ShiroConfig.java +129 -0
  91. package/ark-web/src/main/java/com/epcc/arkweb/filter/LoginFormFilter.java +43 -0
  92. package/ark-web/src/main/java/com/epcc/arkweb/helper/AuthContextHolder.java +39 -0
  93. package/ark-web/src/main/java/com/epcc/arkweb/helper/AuthenticatedRequest.java +91 -0
  94. package/ark-web/src/main/java/com/epcc/arkweb/model/ShiroUser.java +21 -0
  95. package/ark-web/src/main/java/com/epcc/arkweb/utils/ResultMsg.java +19 -0
  96. package/ark-web/src/main/java/com/epcc/arkweb/vo/llm/ScheduledTaskCommandVO.java +30 -0
  97. package/ark-web/src/main/java/com/epcc/arkweb/vo/llm/ScheduledTaskQueryVO.java +20 -0
  98. package/ark-web/src/main/java/com/epcc/arkweb/web/CommonController.java +25 -0
  99. package/ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentAuthorizationInterceptor.java +229 -0
  100. package/ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentController.java +165 -0
  101. package/ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentPermission.java +13 -0
  102. package/ark-web/src/main/java/com/epcc/arkweb/web/llm/LlmController.java +209 -0
  103. package/ark-web/src/main/java/com/epcc/arkweb/web/llm/ScheduledTaskController.java +160 -0
  104. package/ark-web/src/main/java/com/epcc/arkweb/web/sensitive/reveal/SensitiveCaptchaService.java +146 -0
  105. package/ark-web/src/main/java/com/epcc/arkweb/web/sensitive/reveal/SensitiveRevealController.java +163 -0
  106. package/ark-web/src/main/resources/application.properties +1 -0
  107. package/ark-web/src/main/resources/application.yml +23 -0
  108. package/ark-web/src/main/resources/dubbo-consumer.xml +19 -0
  109. package/ark-web/src/production-overlay/java/com/epcc/arkweb/config/CasShiroConfig.java +229 -0
  110. package/ark-web/src/production-overlay/java/com/epcc/arkweb/config/InterceptorConfig.java +35 -0
  111. package/ark-web/src/production-overlay/java/com/epcc/arkweb/config/ShiroConfig.java +161 -0
  112. package/ark-web/src/test/java/com/epcc/arkweb/ArkWebStructureContractTest.java +290 -0
  113. package/ark-web/src/test/java/com/epcc/arkweb/ShiroTestSupport.java +32 -0
  114. package/ark-web/src/test/java/com/epcc/arkweb/config/LocalShiroIntegrationTest.java +121 -0
  115. package/ark-web/src/test/java/com/epcc/arkweb/helper/AuthenticatedRequestTest.java +131 -0
  116. package/ark-web/src/test/java/com/epcc/arkweb/local/LocalArkAuthServiceMockTest.java +17 -0
  117. package/ark-web/src/test/java/com/epcc/arkweb/web/llm/AgentAuthorizationInterceptorTest.java +134 -0
  118. package/ark-web/src/test/java/com/epcc/arkweb/web/llm/AgentControllerAuthorizationTest.java +97 -0
  119. package/ark-web/src/test/java/com/epcc/arkweb/web/llm/LlmControllerTest.java +113 -0
  120. package/ark-web/src/test/java/com/epcc/arkweb/web/sensitive/reveal/SensitiveRevealControllerTest.java +273 -0
  121. package/package.json +18 -0
  122. package/pom.xml +16 -0
@@ -0,0 +1,104 @@
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.sensitive.SensitiveRevealService;
7
+ import org.junit.Test;
8
+ import org.mockito.ArgumentCaptor;
9
+
10
+ import java.util.LinkedHashMap;
11
+ import java.util.Map;
12
+
13
+ import static org.junit.Assert.*;
14
+ import static org.mockito.Matchers.*;
15
+ import static org.mockito.Mockito.*;
16
+
17
+ public class SensitiveDataDemoServiceImplTest {
18
+ private static final String TOKEN = "rt_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
19
+ private static final String MASK = "[#138****8000#VIEW:" + TOKEN + "]";
20
+ private final ObjectMapper json = new ObjectMapper();
21
+ private final SensitiveDataDemoMapper mapper = mock(SensitiveDataDemoMapper.class);
22
+ private final SensitiveRevealService reveal = mock(SensitiveRevealService.class);
23
+ private final SensitiveDataDemoServiceImpl service = new SensitiveDataDemoServiceImpl(mapper, json, reveal);
24
+
25
+ @Test
26
+ public void saveResolvesUnopenedFragmentsPreservesNewlinesAndReturnsOnlyQueriedRow() throws Exception {
27
+ Map<String, Object> input = row("phoneNumber", " 新备注\n" + MASK + " ");
28
+ input.put("id", 7);
29
+ Map<String, Object> saved = row("phoneNumber", " 新备注\n" + MASK + " ");
30
+ saved.put("id", 7L);
31
+ when(reveal.reveal(anyString())).thenReturn("13800138000");
32
+ when(mapper.update(anyMap())).thenReturn(1);
33
+ when(mapper.queryById(7L)).thenReturn(saved);
34
+
35
+ assertSame(saved, service.save(json.writeValueAsString(input)));
36
+
37
+ ArgumentCaptor<Map> persisted = ArgumentCaptor.forClass(Map.class);
38
+ verify(mapper).update(persisted.capture());
39
+ assertEquals(" 新备注\n13800138000 ", persisted.getValue().get("phoneNumber"));
40
+ ArgumentCaptor<String> tokenInput = ArgumentCaptor.forClass(String.class);
41
+ verify(reveal).reveal(tokenInput.capture());
42
+ Map parsed = json.readValue(tokenInput.getValue(), Map.class);
43
+ assertEquals("user-1", parsed.get("userId"));
44
+ assertEquals(TOKEN, parsed.get("token"));
45
+ }
46
+
47
+ @Test
48
+ public void insertReturnsTheGeneratedIdRatherThanTheLatestRow() throws Exception {
49
+ doAnswer(call -> { ((Map) call.getArguments()[0]).put("id", 9L); return 1; })
50
+ .when(mapper).insertSaved(anyMap());
51
+ Map<String, Object> saved = row("phoneNumber", MASK);
52
+ when(mapper.queryById(9L)).thenReturn(saved);
53
+ Map<String, Object> input = row("phoneNumber", "13800138000");
54
+ input.put("id", null);
55
+ assertSame(saved, service.save(json.writeValueAsString(input)));
56
+ verify(mapper, never()).query();
57
+ }
58
+
59
+ @Test
60
+ public void addressBookEditResolvesOnlySensitiveValuesAndReturnsBypassResult() throws Exception {
61
+ Map<String, Object> input = row("name", "新联系人");
62
+ input.put("id", 3); input.put("role", "003"); input.put("email", "demo@example.com");
63
+ input.put("telephone", "010-12345678"); input.put("mobileNumber", MASK);
64
+ when(reveal.reveal(anyString())).thenReturn("13800138000");
65
+ when(mapper.updateAddressBook(any(SensitiveAddressBookDemo.class))).thenReturn(1);
66
+ SensitiveAddressBookDemo saved = new SensitiveAddressBookDemo();
67
+ saved.setId(3L); saved.setRole("003"); saved.setMobileNumber(MASK);
68
+ when(mapper.queryAddressBookById(3L)).thenReturn(saved);
69
+
70
+ assertEquals(MASK, service.saveAddressBook(json.writeValueAsString(input)).get("mobileNumber"));
71
+ ArgumentCaptor<SensitiveAddressBookDemo> persisted = ArgumentCaptor.forClass(SensitiveAddressBookDemo.class);
72
+ verify(mapper).updateAddressBook(persisted.capture());
73
+ assertEquals("13800138000", persisted.getValue().getMobileNumber());
74
+ assertEquals("新联系人", persisted.getValue().getName());
75
+ }
76
+
77
+ @Test
78
+ public void expiredOrDamagedFragmentsNeverPersist() throws Exception {
79
+ when(reveal.reveal(anyString())).thenThrow(new SensitiveRevealService.ExpiredTokenException());
80
+ try { service.save(json.writeValueAsString(row("phoneNumber", MASK))); fail(); }
81
+ catch (SensitiveRevealService.ExpiredTokenException expected) { }
82
+ try { service.save(json.writeValueAsString(row("phoneNumber", "[#138****8000]"))); fail(); }
83
+ catch (IllegalArgumentException expected) { }
84
+ verifyZeroInteractions(mapper);
85
+ }
86
+
87
+ @Test
88
+ public void rejectsInvalidIdsAndOverlongResolvedContent() throws Exception {
89
+ for (Object id : new Object[]{0, -1, 1.5, "1"}) {
90
+ Map<String, Object> input = row("phoneNumber", "13800138000"); input.put("id", id);
91
+ try { service.save(json.writeValueAsString(input)); fail("accepted invalid id"); }
92
+ catch (IllegalArgumentException expected) { }
93
+ }
94
+ when(reveal.reveal(anyString())).thenReturn(new String(new char[65]).replace('\0', '1'));
95
+ try { service.save(json.writeValueAsString(row("phoneNumber", MASK))); fail(); }
96
+ catch (IllegalArgumentException expected) { }
97
+ verifyZeroInteractions(mapper);
98
+ }
99
+
100
+ private Map<String, Object> row(String key, String value) {
101
+ Map<String, Object> result = new LinkedHashMap<>();
102
+ result.put("userId", "user-1"); result.put(key, value); return result;
103
+ }
104
+ }
@@ -0,0 +1,65 @@
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.junit.Test;
6
+
7
+ import java.util.Arrays;
8
+ import java.util.HashMap;
9
+ import java.util.Map;
10
+
11
+ import static org.junit.Assert.assertEquals;
12
+ import static org.junit.Assert.assertFalse;
13
+ import static org.junit.Assert.assertTrue;
14
+
15
+ public class RedisRevealTokenStoreTest {
16
+ @Test
17
+ public void storesCiphertextThroughProductionRedisContract() {
18
+ RecordingRedis redis = new RecordingRedis();
19
+ RedisRevealTokenStore store = new RedisRevealTokenStore(redis, 300);
20
+
21
+ assertTrue(store.putAll(Arrays.asList(
22
+ new RedisRevealTokenStore.Entry("rt_A", "cipher-a"),
23
+ new RedisRevealTokenStore.Entry("rt_B", "cipher-b"))));
24
+ assertEquals(2, redis.values.size());
25
+ assertEquals(Integer.valueOf(300), redis.ttls.get(RedisRevealTokenStore.key("rt_A")));
26
+ assertEquals("cipher-a", store.get("rt_A"));
27
+ }
28
+
29
+ @Test
30
+ public void redisFailureDoesNotPublishRevealTokens() {
31
+ RecordingRedis redis = new RecordingRedis();
32
+ redis.failWrites = true;
33
+
34
+ assertFalse(new RedisRevealTokenStore(redis, 300).putAll(Arrays.asList(
35
+ new RedisRevealTokenStore.Entry("rt_A", "cipher-a"))));
36
+ }
37
+
38
+ @Test(expected = RedisRevealTokenStore.StoreUnavailableException.class)
39
+ public void redisReadFailureIsNotReportedAsAnExpiredToken() {
40
+ RecordingRedis redis = new RecordingRedis();
41
+ redis.failReads = true;
42
+
43
+ new RedisRevealTokenStore(redis, 300).get("rt_A");
44
+ }
45
+
46
+ static final class RecordingRedis extends RedisCacheService {
47
+ final Map<String, String> values = new HashMap<>();
48
+ final Map<String, Integer> ttls = new HashMap<>();
49
+ boolean failWrites;
50
+ boolean failReads;
51
+
52
+ @Override
53
+ public String setex(String key, int time, String value) {
54
+ if (failWrites) return ResultUtil.FAIL_RESULT;
55
+ values.put(key, value);
56
+ ttls.put(key, time);
57
+ return ResultUtil.SUCCESS_RESULT;
58
+ }
59
+
60
+ @Override
61
+ public String get(String key) {
62
+ return failReads ? ResultUtil.FAIL_RESULT : values.get(key);
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,99 @@
1
+ package com.union.control.service.sensitive;
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.fasterxml.jackson.databind.ObjectMapper;
7
+ import com.nucc.channel.ark.common.exception.BaseDataErrorCode;
8
+ import com.nucc.channel.ark.common.exception.CheckException;
9
+ import com.nucc.channel.ark.common.redis.RedisCacheService;
10
+ import com.union.control.mapper.interceptor.AESInterceptor;
11
+ import com.union.control.utils.security.SymmetricalSecurityUtils;
12
+ import org.junit.Test;
13
+ import org.springframework.beans.factory.annotation.Autowired;
14
+ import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
15
+
16
+ import java.nio.charset.StandardCharsets;
17
+
18
+ import static org.junit.Assert.assertEquals;
19
+ import static org.junit.Assert.assertFalse;
20
+ import static org.junit.Assert.assertNotNull;
21
+ import static org.junit.Assert.assertTrue;
22
+
23
+ public class SensitiveRevealBoundaryTest {
24
+ @Test
25
+ public void springSelectsTheSensitiveRevealProcessorProductionConstructor() {
26
+ java.lang.reflect.Constructor<?>[] constructors =
27
+ new AutowiredAnnotationBeanPostProcessor().determineCandidateConstructors(
28
+ SensitiveRevealProcessor.class, "sensitiveRevealProcessor");
29
+
30
+ assertNotNull(constructors);
31
+ assertEquals(1, constructors.length);
32
+ assertEquals(2, constructors[0].getParameterTypes().length);
33
+ }
34
+
35
+ @Test
36
+ public void sensitiveInterceptorDependenciesAreRequired() throws Exception {
37
+ assertTrue(AESInterceptor.class.getDeclaredField("processor")
38
+ .getAnnotation(Autowired.class).required());
39
+ assertTrue(AESInterceptor.class.getDeclaredField("addressBook")
40
+ .getAnnotation(Autowired.class).required());
41
+ }
42
+
43
+ @Test
44
+ public void redisKeyContainsOnlyTheTokenHash() {
45
+ String token = "rt_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
46
+ String key = RedisRevealTokenStore.key(token);
47
+ assertTrue(key.matches("sensitive:reveal:[0-9a-f]{64}"));
48
+ assertFalse(key.contains(token));
49
+ }
50
+
51
+ @Test
52
+ public void revealReturnsOnlyTheEncryptedFragment() throws Exception {
53
+ SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(new PrefixGateway());
54
+ final String ciphertext = crypto.encryptWithCheck("13800138000");
55
+ RedisRevealTokenStore store = new RedisRevealTokenStore(new RedisCacheService() {
56
+ @Override
57
+ public String get(String key) {
58
+ return ciphertext;
59
+ }
60
+ }, 300);
61
+ String token = "rt_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
62
+
63
+ assertEquals("13800138000", new SensitiveRevealServiceImpl(store, crypto, new ObjectMapper())
64
+ .reveal("{\"userId\":\"ignored-by-first-version\",\"token\":\"" + token + "\"}"));
65
+ }
66
+
67
+ @Test(expected = SensitiveRevealService.InvalidTokenException.class)
68
+ public void malformedTokenNeverReachesTheStore() {
69
+ RedisRevealTokenStore store = new RedisRevealTokenStore(new RedisCacheService() {
70
+ @Override
71
+ public String get(String key) {
72
+ throw new AssertionError("store must not be called");
73
+ }
74
+ }, 300);
75
+ new SensitiveRevealServiceImpl(store,
76
+ new SymmetricalSecurityUtils(new PrefixGateway()), new ObjectMapper())
77
+ .reveal("{\"userId\":\"user-1\",\"token\":\"bad\"}");
78
+ }
79
+
80
+ static class PrefixGateway implements SymmetricalSecurityService {
81
+ @Override
82
+ public Result<SecurityResult> encryptByFixedKey(Algorithm algorithm, byte[] plaintext) {
83
+ byte[] prefix = "enc:".getBytes(StandardCharsets.UTF_8);
84
+ byte[] result = new byte[prefix.length + plaintext.length];
85
+ System.arraycopy(prefix, 0, result, 0, prefix.length);
86
+ System.arraycopy(plaintext, 0, result, prefix.length, plaintext.length);
87
+ return Result.success(new SecurityResult(result));
88
+ }
89
+
90
+ @Override
91
+ public Result<SecurityResult> decryptByFixedKey(Algorithm algorithm, byte[] ciphertext) {
92
+ if (ciphertext.length < 4) {
93
+ return Result.failure(BaseDataErrorCode.SYSTEM_INNER_ERROR.getCode(), "invalid");
94
+ }
95
+ return Result.success(new SecurityResult(
96
+ java.util.Arrays.copyOfRange(ciphertext, 4, ciphertext.length)));
97
+ }
98
+ }
99
+ }
@@ -0,0 +1,111 @@
1
+ package com.union.control.service.sensitive;
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.exception.BaseDataErrorCode;
8
+ import com.nucc.channel.ark.common.exception.CheckException;
9
+ import com.union.control.utils.security.SymmetricalSecurityUtils;
10
+ import org.junit.Test;
11
+
12
+ import java.nio.charset.StandardCharsets;
13
+ import java.util.ArrayList;
14
+ import java.util.Base64;
15
+ import java.util.HashMap;
16
+ import java.util.List;
17
+ import java.util.Map;
18
+
19
+ import static org.junit.Assert.assertEquals;
20
+
21
+ public class SensitiveRevealProcessorCodecTest {
22
+ @Test
23
+ public void encryptsEveryElementOfADirectListParameter() throws Exception {
24
+ SensitiveRevealProcessor codec = processor(new PrefixGateway());
25
+ List<Contact> contacts = new ArrayList<>();
26
+ contacts.add(new Contact("13800138000"));
27
+ contacts.add(new Contact("13900139000"));
28
+
29
+ codec.encrypt(contacts);
30
+
31
+ assertEquals(ciphertext("13800138000"), contacts.get(0).mobile);
32
+ assertEquals(ciphertext("13900139000"), contacts.get(1).mobile);
33
+ }
34
+
35
+ @Test
36
+ public void encryptsSensitiveKeysInAPlainMapParameter() throws Exception {
37
+ SensitiveRevealProcessor codec = processor(new PrefixGateway());
38
+ Map<String, Object> parameter = new HashMap<>();
39
+ parameter.put("email", "demo@example.com");
40
+ parameter.put("role", "001");
41
+
42
+ codec.encrypt(parameter);
43
+
44
+ assertEquals(ciphertext("demo@example.com"), parameter.get("email"));
45
+ assertEquals("001", parameter.get("role"));
46
+ }
47
+
48
+ @Test
49
+ public void keepsMyBatisScalarAliasesOnTheSameCiphertext() throws Exception {
50
+ SensitiveRevealProcessor codec = processor(new PrefixGateway());
51
+ Map<String, Object> parameter = new HashMap<>();
52
+ String email = new String("demo@example.com");
53
+ parameter.put("email", email);
54
+ parameter.put("param1", email);
55
+
56
+ codec.encrypt(parameter);
57
+
58
+ assertEquals(ciphertext("demo@example.com"), parameter.get("email"));
59
+ assertEquals(parameter.get("email"), parameter.get("param1"));
60
+ }
61
+
62
+ @Test(expected = CheckException.class)
63
+ public void encryptionFailureIsNotSwallowed() throws Exception {
64
+ SensitiveRevealProcessor codec = processor(new FailingGateway());
65
+ codec.encrypt(new Contact("13800138000"));
66
+ }
67
+
68
+ private static SensitiveRevealProcessor processor(SymmetricalSecurityService gateway) {
69
+ return new SensitiveRevealProcessor(new SymmetricalSecurityUtils(gateway), null);
70
+ }
71
+
72
+ private static String ciphertext(String plaintext) {
73
+ return Base64.getEncoder().encodeToString(
74
+ ("enc:" + plaintext).getBytes(StandardCharsets.UTF_8));
75
+ }
76
+
77
+ static class Contact {
78
+ @EnDecryptField
79
+ String mobile;
80
+
81
+ Contact(String mobile) {
82
+ this.mobile = mobile;
83
+ }
84
+ }
85
+
86
+ static class PrefixGateway implements SymmetricalSecurityService {
87
+ @Override
88
+ public Result<SecurityResult> encryptByFixedKey(Algorithm algorithm, byte[] plaintext) {
89
+ return Result.success(new SecurityResult(
90
+ ("enc:" + new String(plaintext, StandardCharsets.UTF_8))
91
+ .getBytes(StandardCharsets.UTF_8)));
92
+ }
93
+
94
+ @Override
95
+ public Result<SecurityResult> decryptByFixedKey(Algorithm algorithm, byte[] ciphertext) {
96
+ return Result.success(new SecurityResult(ciphertext));
97
+ }
98
+ }
99
+
100
+ static class FailingGateway implements SymmetricalSecurityService {
101
+ @Override
102
+ public Result<SecurityResult> encryptByFixedKey(Algorithm algorithm, byte[] plaintext) {
103
+ return Result.failure(BaseDataErrorCode.SYSTEM_INNER_ERROR.getCode(), "unavailable");
104
+ }
105
+
106
+ @Override
107
+ public Result<SecurityResult> decryptByFixedKey(Algorithm algorithm, byte[] ciphertext) {
108
+ return Result.failure(BaseDataErrorCode.SYSTEM_INNER_ERROR.getCode(), "unavailable");
109
+ }
110
+ }
111
+ }
@@ -0,0 +1,111 @@
1
+ package com.union.control.service.sensitive;
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.exception.BaseDataErrorCode;
7
+ import com.nucc.channel.ark.common.redis.RedisCacheService;
8
+ import com.nucc.channel.ark.common.util.ResultUtil;
9
+ import com.union.control.utils.security.SymmetricalSecurityUtils;
10
+ import org.junit.Test;
11
+
12
+ import java.nio.charset.StandardCharsets;
13
+ import java.util.ArrayList;
14
+ import java.util.Base64;
15
+ import java.util.HashMap;
16
+ import java.util.List;
17
+ import java.util.Map;
18
+
19
+ import static org.junit.Assert.assertEquals;
20
+ import static org.junit.Assert.assertFalse;
21
+ import static org.junit.Assert.assertTrue;
22
+
23
+ public class SensitiveRevealProcessorTest {
24
+ @Test
25
+ public void masksEachMatchAndWritesOneCiphertextBatch() throws Exception {
26
+ RecordingRedis redis = new RecordingRedis();
27
+ SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(new PrefixGateway());
28
+ SensitiveRevealProcessor processor = new SensitiveRevealProcessor(
29
+ crypto, new RedisRevealTokenStore(redis, 300));
30
+ Map<String, Object> row = row(crypto, "手机13800138000,邮箱demo@example.com");
31
+
32
+ processor.process(row);
33
+
34
+ String value = (String) row.get("phoneNumber");
35
+ assertTrue(value, value.matches("手机\\[#138\\*{4}8000#VIEW:rt_[A-Za-z0-9_-]{43}\\],"
36
+ + "邮箱\\[#d\\*{3}@example\\.com#VIEW:rt_[A-Za-z0-9_-]{43}\\]"));
37
+ assertEquals(2, redis.values.size());
38
+ assertTrue(redis.values.get(0).startsWith("ZW5j"));
39
+ assertFalse(redis.values.get(0).contains("13800138000"));
40
+ }
41
+
42
+ @Test
43
+ public void storeFailureReturnsMaskedValuesWithoutTokens() throws Exception {
44
+ RecordingRedis redis = new RecordingRedis();
45
+ redis.succeed = false;
46
+ SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(new PrefixGateway());
47
+ SensitiveRevealProcessor processor = new SensitiveRevealProcessor(
48
+ crypto, new RedisRevealTokenStore(redis, 300));
49
+ Map<String, Object> row = row(crypto, "手机13800138000");
50
+
51
+ processor.process(row);
52
+
53
+ assertEquals("手机[#138****8000]", row.get("phoneNumber"));
54
+ }
55
+
56
+ @Test
57
+ public void fragmentEncryptionFailureNeverPublishesAnUnstoredToken() {
58
+ RecordingRedis redis = new RecordingRedis();
59
+ SensitiveRevealProcessor processor = new SensitiveRevealProcessor(
60
+ new SymmetricalSecurityUtils(new FailingEncryptGateway()),
61
+ new RedisRevealTokenStore(redis, 300));
62
+ Map<String, Object> row = new HashMap<>();
63
+ row.put("phoneNumber", Base64.getEncoder().encodeToString(
64
+ "enc:13800138000".getBytes(StandardCharsets.UTF_8)));
65
+
66
+ processor.process(row);
67
+
68
+ assertEquals("[#138****8000]", row.get("phoneNumber"));
69
+ assertTrue(redis.values.isEmpty());
70
+ }
71
+
72
+ private static Map<String, Object> row(SymmetricalSecurityUtils crypto, String plaintext)
73
+ throws Exception {
74
+ Map<String, Object> row = new HashMap<>();
75
+ row.put("phoneNumber", crypto.encryptWithCheck(plaintext));
76
+ return row;
77
+ }
78
+
79
+ static class RecordingRedis extends RedisCacheService {
80
+ boolean succeed = true;
81
+ final List<String> values = new ArrayList<>();
82
+
83
+ @Override
84
+ public String setex(String key, int time, String value) {
85
+ values.add(value);
86
+ return succeed ? ResultUtil.SUCCESS_RESULT : ResultUtil.FAIL_RESULT;
87
+ }
88
+ }
89
+
90
+ static class PrefixGateway implements SymmetricalSecurityService {
91
+ @Override
92
+ public Result<SecurityResult> encryptByFixedKey(Algorithm algorithm, byte[] plaintext) {
93
+ return Result.success(new SecurityResult(
94
+ ("enc:" + new String(plaintext, StandardCharsets.UTF_8))
95
+ .getBytes(StandardCharsets.UTF_8)));
96
+ }
97
+
98
+ @Override
99
+ public Result<SecurityResult> decryptByFixedKey(Algorithm algorithm, byte[] ciphertext) {
100
+ return Result.success(new SecurityResult(
101
+ java.util.Arrays.copyOfRange(ciphertext, 4, ciphertext.length)));
102
+ }
103
+ }
104
+
105
+ static class FailingEncryptGateway extends PrefixGateway {
106
+ @Override
107
+ public Result<SecurityResult> encryptByFixedKey(Algorithm algorithm, byte[] plaintext) {
108
+ return Result.failure(BaseDataErrorCode.SYSTEM_INNER_ERROR.getCode(), "unavailable");
109
+ }
110
+ }
111
+ }
@@ -0,0 +1,87 @@
1
+ package com.union.control.utils.security;
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.exception.CheckException;
7
+ import org.junit.Test;
8
+
9
+ import java.nio.charset.StandardCharsets;
10
+ import java.util.ArrayList;
11
+ import java.util.List;
12
+
13
+ import static org.junit.Assert.assertEquals;
14
+ import static org.junit.Assert.assertTrue;
15
+
16
+ public class SymmetricalSecurityUtilsCompatibilityTest {
17
+ @Test
18
+ public void longValuesUseTheConfiguredChunkSizeAndRoundTrip() throws Exception {
19
+ RecordingProxy proxy = new RecordingProxy();
20
+ SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(proxy);
21
+
22
+ String ciphertext = crypto.encryptLongString("abcdefgh", 3);
23
+
24
+ assertEquals(3, proxy.encrypted.size());
25
+ assertEquals("abc", proxy.encrypted.get(0));
26
+ assertEquals("def", proxy.encrypted.get(1));
27
+ assertEquals("gh", proxy.encrypted.get(2));
28
+ assertEquals("abcdefgh", crypto.decryptLongString(ciphertext));
29
+ }
30
+
31
+ @Test
32
+ public void taggedValuesKeepTheProductionDatabaseMarkerDirection() throws Exception {
33
+ RecordingProxy proxy = new RecordingProxy();
34
+ SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(proxy);
35
+ String plaintext = "手机13800138000 邮箱demo@example.com 电话010-12345678";
36
+
37
+ String ciphertext = crypto.encryptWithTag(plaintext);
38
+
39
+ assertTrue(ciphertext.contains("#["));
40
+ assertEquals(3, occurrences(ciphertext, "#["));
41
+ assertEquals(plaintext, crypto.decryptWithTag(ciphertext));
42
+ }
43
+
44
+ @Test
45
+ public void proxyFailureKeepsTheProductionCheckedErrorContract() {
46
+ SymmetricalSecurityUtils crypto = new SymmetricalSecurityUtils(new RecordingProxy() {
47
+ @Override
48
+ public Result<SecurityResult> encryptByFixedKey(Algorithm algorithm, byte[] plaintext) {
49
+ return Result.failure("REMOTE_ERROR", "unavailable");
50
+ }
51
+ });
52
+
53
+ try {
54
+ crypto.encryptWithCheck("13800138000");
55
+ throw new AssertionError("expected CheckException");
56
+ } catch (CheckException error) {
57
+ assertEquals("SYSTEM_INNER_ERROR", error.getCode());
58
+ assertTrue(error.getMessage().contains("REMOTE_ERROR"));
59
+ }
60
+ }
61
+
62
+ private static int occurrences(String value, String needle) {
63
+ return (value.length() - value.replace(needle, "").length()) / needle.length();
64
+ }
65
+
66
+ static class RecordingProxy implements SymmetricalSecurityService {
67
+ final List<String> encrypted = new ArrayList<>();
68
+
69
+ @Override
70
+ public Result<SecurityResult> encryptByFixedKey(Algorithm algorithm, byte[] plaintext) {
71
+ assertEquals(Algorithm.AES256, algorithm);
72
+ encrypted.add(new String(plaintext, StandardCharsets.UTF_8));
73
+ byte[] result = new byte[plaintext.length + 1];
74
+ result[0] = 42;
75
+ System.arraycopy(plaintext, 0, result, 1, plaintext.length);
76
+ return Result.success(new SecurityResult(result));
77
+ }
78
+
79
+ @Override
80
+ public Result<SecurityResult> decryptByFixedKey(Algorithm algorithm, byte[] ciphertext) {
81
+ assertEquals(Algorithm.AES256, algorithm);
82
+ byte[] result = new byte[ciphertext.length - 1];
83
+ System.arraycopy(ciphertext, 1, result, 0, result.length);
84
+ return Result.success(new SecurityResult(result));
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,16 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project xmlns="http://maven.apache.org/POM/4.0.0"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5
+ <modelVersion>4.0.0</modelVersion>
6
+ <groupId>com.union</groupId>
7
+ <artifactId>ark-control-facade</artifactId>
8
+ <version>1.0.0</version>
9
+ <packaging>jar</packaging>
10
+
11
+ <properties>
12
+ <maven.compiler.source>1.8</maven.compiler.source>
13
+ <maven.compiler.target>1.8</maven.compiler.target>
14
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15
+ </properties>
16
+ </project>
@@ -0,0 +1,8 @@
1
+ package com.union.control.service;
2
+
3
+ import java.util.Map;
4
+
5
+ public interface AgentExecutionService {
6
+ Map<String, Object> claimAguiRun(String payload);
7
+ Map<String, Object> completeRun(String input);
8
+ }
@@ -0,0 +1,7 @@
1
+ package com.union.control.service;
2
+
3
+ public interface AgentProxyService {
4
+ AgentResponse sync(String cookie, String payload);
5
+ AgentResponse scheduled(String authorization);
6
+ AgentResponse cancel(String cookie, String conversationId, String runId);
7
+ }
@@ -0,0 +1,22 @@
1
+ package com.union.control.service;
2
+
3
+ import java.io.Serializable;
4
+
5
+ /** Serializable HTTP result carried by non-stream Agent Dubbo methods. */
6
+ public final class AgentResponse implements Serializable {
7
+ private static final long serialVersionUID = 1L;
8
+ private int status;
9
+ private String body;
10
+
11
+ public AgentResponse() {}
12
+
13
+ public AgentResponse(int status, String body) {
14
+ this.status = status;
15
+ this.body = body;
16
+ }
17
+
18
+ public int getStatus() { return status; }
19
+ public void setStatus(int status) { this.status = status; }
20
+ public String getBody() { return body; }
21
+ public void setBody(String body) { this.body = body; }
22
+ }
@@ -0,0 +1,12 @@
1
+ package com.union.control.service;
2
+
3
+ import java.util.Map;
4
+
5
+ public interface ConversationService {
6
+ Map<String, Object> userInfo(String input);
7
+ Map<String, Object> conversations(String input);
8
+ Map<String, Object> conversation(String input);
9
+ Map<String, Object> conversationMessages(String input);
10
+ Map<String, Object> rename(String input);
11
+ Map<String, Object> deleteConversation(String input);
12
+ }
@@ -0,0 +1,12 @@
1
+ package com.union.control.service;
2
+
3
+ import java.util.Map;
4
+
5
+ public interface MemoryStoreService {
6
+ Map<String, Object> memoryRead(String input);
7
+ Map<String, Object> memoryList(String input);
8
+ Map<String, Object> memoryOperation(String input);
9
+ Map<String, Object> memoryWrite(String input);
10
+ Map<String, Object> memoryDelete(String input);
11
+ Map<String, Object> memorySearch(String input);
12
+ }
@@ -0,0 +1,10 @@
1
+ package com.union.control.service;
2
+
3
+ import java.util.Map;
4
+
5
+ public interface RunningAnalysisMockService {
6
+ Map<String, Object> getOrgInfo(String input);
7
+ Map<String, Object> queryBigData(String input);
8
+ Map<String, Object> announceList(String input);
9
+ Map<String, Object> getJiraInfo(String input);
10
+ }