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,290 @@
1
+ package com.epcc.arkweb;
2
+
3
+ import org.junit.Test;
4
+ import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
5
+
6
+ import com.epcc.arkweb.web.llm.AgentController;
7
+ import com.epcc.arkweb.helper.AuthenticatedRequest;
8
+ import com.union.control.service.AgentExecutionService;
9
+ import com.union.control.service.ConversationService;
10
+ import com.union.control.service.MemoryStoreService;
11
+ import com.union.control.service.RunningAnalysisMockService;
12
+ import com.epcc.arkweb.web.llm.AgentAuthorizationInterceptor;
13
+ import com.epcc.arkweb.web.llm.AgentPermission;
14
+ import org.springframework.web.bind.annotation.PostMapping;
15
+
16
+ import java.nio.charset.StandardCharsets;
17
+ import java.nio.file.Files;
18
+ import java.nio.file.Path;
19
+ import java.nio.file.Paths;
20
+ import java.util.Arrays;
21
+ import java.util.List;
22
+ import java.util.stream.Collectors;
23
+
24
+ import static org.assertj.core.api.Assertions.assertThat;
25
+
26
+ public class ArkWebStructureContractTest {
27
+
28
+ private static final Path ROOT = Paths.get("src/main/java");
29
+ private static final Path LOCAL_MOCK = Paths.get("src/local-mock/java");
30
+ private static final Path PRODUCTION_OVERLAY = Paths.get("src/production-overlay/java");
31
+ private static final Path WORKSPACE = Paths.get("..");
32
+
33
+ @Test
34
+ public void dubboTimeoutBindsTheSharedPyDeadline() {
35
+ for (String seconds : new String[]{null, "2.5"}) {
36
+ org.springframework.beans.factory.support.DefaultListableBeanFactory beans =
37
+ new org.springframework.beans.factory.support.DefaultListableBeanFactory();
38
+ beans.setBeanExpressionResolver(new org.springframework.context.expression.StandardBeanExpressionResolver());
39
+ new org.springframework.beans.factory.xml.XmlBeanDefinitionReader(beans)
40
+ .loadBeanDefinitions("classpath:dubbo-consumer.xml");
41
+ java.util.Properties values = new java.util.Properties();
42
+ values.setProperty("dubbo.application.name", "timeout-test");
43
+ values.setProperty("dubbo.registry.address", "N/A");
44
+ values.setProperty("dubbo.consumer.timeout-ms", "10000");
45
+ values.setProperty("dubbo.consumer.check", "false");
46
+ values.setProperty("dubbo.consumer.direct-url", "");
47
+ if (seconds != null) values.setProperty("AGENT_MAX_RUN_SECONDS", seconds);
48
+ org.springframework.context.support.PropertySourcesPlaceholderConfigurer placeholders =
49
+ new org.springframework.context.support.PropertySourcesPlaceholderConfigurer();
50
+ placeholders.setEnvironment(new org.springframework.mock.env.MockEnvironment());
51
+ placeholders.setProperties(values);
52
+ placeholders.postProcessBeanFactory(beans);
53
+ try {
54
+ com.alibaba.dubbo.config.spring.ReferenceBean<?> reference =
55
+ (com.alibaba.dubbo.config.spring.ReferenceBean<?>) beans.getBean("&agentProxyService");
56
+ assertThat(reference.getTimeout()).isEqualTo(seconds == null ? 900000 : 2500);
57
+ } finally {
58
+ beans.destroySingletons();
59
+ }
60
+ }
61
+ }
62
+
63
+ @Test
64
+ public void localApplicationUsesTheProductionRootPackage() throws Exception {
65
+ String source = source("com/epcc/arkweb/Application.java");
66
+ assertThat(source)
67
+ .contains("package com.epcc.arkweb;")
68
+ .contains("class Application")
69
+ .contains("@ImportResource(\"classpath:dubbo-consumer.xml\")")
70
+ .doesNotContain("MapperScan")
71
+ .doesNotContain("com.union.control.mapper");
72
+ assertThat(ROOT.resolve("com/union/control")).doesNotExist();
73
+ }
74
+
75
+ @Test
76
+ public void allLlmControllersLiveAtTheProductionWebLayer() throws Exception {
77
+ for (String controller : Arrays.asList(
78
+ "LlmController.java", "AgentController.java", "ScheduledTaskController.java")) {
79
+ Path expected = ROOT.resolve("com/epcc/arkweb/web/llm").resolve(controller);
80
+ assertThat(expected).exists();
81
+ }
82
+
83
+ List<Path> serviceControllers = Files.walk(WORKSPACE.resolve("ark-control/src/main/java"))
84
+ .filter(Files::isRegularFile)
85
+ .filter(path -> path.getFileName().toString().endsWith("Controller.java"))
86
+ .collect(Collectors.toList());
87
+ assertThat(serviceControllers).isEmpty();
88
+
89
+ }
90
+
91
+ @Test
92
+ public void springSelectsTheAgentControllerProductionConstructor() {
93
+ java.lang.reflect.Constructor<?>[] constructors = new AutowiredAnnotationBeanPostProcessor()
94
+ .determineCandidateConstructors(AgentController.class, "agentController");
95
+ assertThat(constructors).hasSize(1);
96
+ assertThat(constructors[0].getParameterTypes()).containsExactly(
97
+ ConversationService.class, AgentExecutionService.class,
98
+ MemoryStoreService.class, RunningAnalysisMockService.class,
99
+ AuthenticatedRequest.class, AgentAuthorizationInterceptor.class);
100
+ }
101
+
102
+ @Test
103
+ public void controllerMatchesProductionShiroAndRouteConventions() throws Exception {
104
+ String source = source("com/epcc/arkweb/web/llm/ScheduledTaskController.java");
105
+
106
+ assertThat(source)
107
+ .contains("package com.epcc.arkweb.web.llm;")
108
+ .contains("@RequestMapping(value = {\"llm\", \"union-op/llm\"})")
109
+ .contains("@RequiresPermissions(value = \"/assistantManager/page\")")
110
+ .contains("AuthenticatedRequest")
111
+ .contains("request.json(")
112
+ .doesNotContain("@RequestHeader")
113
+ .doesNotContain("HttpHeaders.COOKIE")
114
+ .doesNotContain("CASSESSIONID")
115
+ .doesNotContain("agent:execute")
116
+ .doesNotContain("ScheduledExecutionToken");
117
+ }
118
+
119
+ @Test
120
+ public void authenticationLivesInWebAndNotInService() throws Exception {
121
+ String service = Files.walk(WORKSPACE.resolve("ark-control/src/main/java"))
122
+ .filter(Files::isRegularFile)
123
+ .map(path -> {
124
+ try {
125
+ return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
126
+ } catch (Exception error) {
127
+ throw new IllegalStateException(error);
128
+ }
129
+ })
130
+ .collect(Collectors.joining("\n"));
131
+
132
+ assertThat(service)
133
+ .doesNotContain("org.apache.shiro")
134
+ .doesNotContain("AuthContextHolder")
135
+ .doesNotContain("currentUserId()");
136
+ assertThat(ROOT.resolve("com/epcc/arkweb/helper/AuthenticatedRequest.java")).exists();
137
+ assertThat(ROOT.resolve("com/epcc/arkweb/config/ShiroConfig.java")).exists();
138
+ assertThat(ROOT.resolve("com/epcc/arkweb/config/Realm.java")).exists();
139
+ assertThat(ROOT.resolve("com/epcc/arkweb/filter/LoginFormFilter.java")).exists();
140
+ assertThat(ROOT.resolve("com/epcc/arkweb/config/LocalCasRealm.java")).doesNotExist();
141
+ assertThat(ROOT.resolve("com/epcc/arkweb/config/LocalShiroConfig.java")).doesNotExist();
142
+ assertThat(ROOT.resolve("com/epcc/arkweb/config/ArkRealm.java")).doesNotExist();
143
+ assertThat(ROOT.resolve("com/epcc/arkweb/filter/AgentAuthenticationFilter.java")).doesNotExist();
144
+ assertThat(ROOT.resolve("com/epcc/arkweb/config/ScheduledExecutionRealm.java")).doesNotExist();
145
+ assertThat(ROOT.resolve("com/epcc/arkweb/config/AgentAuthorizationConfig.java"))
146
+ .doesNotExist();
147
+ assertThat(source("com/epcc/arkweb/config/InterceptorConfig.java"))
148
+ .contains("addPathPatterns(\"/agent/**\")")
149
+ .doesNotContain("SecurityManager");
150
+ assertThat(LOCAL_MOCK.resolve("com/epcc/arkweb/mock/ArkAuthServiceMock.java"))
151
+ .exists();
152
+ assertThat(ROOT.resolve("com/epcc/arkweb/local")).doesNotExist();
153
+ }
154
+
155
+ @Test
156
+ public void productionShiroCopiesWhitelistAgentBeforeTheCatchAll() throws Exception {
157
+ for (String name : Arrays.asList("CasShiroConfig.java", "ShiroConfig.java")) {
158
+ Path file = PRODUCTION_OVERLAY.resolve("com/epcc/arkweb/config").resolve(name);
159
+ assertThat(file).exists();
160
+ String source = new String(Files.readAllBytes(file), StandardCharsets.UTF_8);
161
+ int agent = source.indexOf("filterMap.put(\"/agent/**\", \"anon\")");
162
+ int catchAll = source.indexOf("filterMap.put(\"/**\",\"authc\")");
163
+ if (catchAll < 0)
164
+ catchAll = source.indexOf("filterMap.put(\"/**\", \"authc\")");
165
+ assertThat(agent).as(name).isGreaterThanOrEqualTo(0);
166
+ assertThat(agent).as(name).isLessThan(catchAll);
167
+ assertThat(source).doesNotContain("ScheduledExecutionRealm")
168
+ .doesNotContain("ScheduledExecutionFilter");
169
+ }
170
+
171
+ String interceptors = new String(Files.readAllBytes(PRODUCTION_OVERLAY.resolve(
172
+ "com/epcc/arkweb/config/InterceptorConfig.java")), StandardCharsets.UTF_8);
173
+ assertThat(interceptors)
174
+ .contains("registry.addInterceptor(agentAuthorizationInterceptor)")
175
+ .contains("addPathPatterns(\"/agent/**\")")
176
+ .contains("\"/logout\", \"/agent/**\"")
177
+ .contains("\"/healthcheck.html\", \"/logout\", \"/agent/**\"");
178
+ }
179
+
180
+ @Test
181
+ public void scheduledAuthorizationReturnsTheCompleteTrustedSnapshot() throws Exception {
182
+ String source = source("com/epcc/arkweb/web/llm/AgentController.java");
183
+
184
+ assertThat(source)
185
+ .contains("@RequestMapping(\"/agent\")")
186
+ .contains("@PostMapping(\"/scheduledTaskAuthorize\")")
187
+ .contains("data.put(\"trustedContext\", authorization)")
188
+ .contains("new LinkedHashMap<>(context)")
189
+ .contains("this.authorization.authorize(");
190
+ }
191
+
192
+ @Test
193
+ public void everyAgentToolDeclaresItsPermission() {
194
+ for (java.lang.reflect.Method method : AgentController.class.getDeclaredMethods()) {
195
+ if (method.getAnnotation(PostMapping.class) == null
196
+ || method.getName().equals("scheduledTaskAuthorize")) continue;
197
+ AgentPermission permission = method.getAnnotation(AgentPermission.class);
198
+ assertThat(permission).as(method.getName()).isNotNull();
199
+ assertThat(permission.value()).as(method.getName())
200
+ .isEqualTo("/assistantManager/page");
201
+ }
202
+ }
203
+
204
+ @Test
205
+ public void browserCommandsCannotDeserializeTrustedIdentity() throws Exception {
206
+ String command = source("com/epcc/arkweb/vo/llm/ScheduledTaskCommandVO.java");
207
+ String query = source("com/epcc/arkweb/vo/llm/ScheduledTaskQueryVO.java");
208
+
209
+ for (String forbidden : Arrays.asList("userId", "loginName", "orgCode",
210
+ "roleId", "permission", "executionToken", "cookie")) {
211
+ assertThat(command).doesNotContain(forbidden);
212
+ assertThat(query).doesNotContain(forbidden);
213
+ }
214
+ }
215
+
216
+ @Test
217
+ public void webOwnsAuthenticationButNotSchedulingOrMappers() throws Exception {
218
+ List<String> files = Files.walk(ROOT)
219
+ .filter(Files::isRegularFile)
220
+ .map(path -> path.getFileName().toString())
221
+ .collect(Collectors.toList());
222
+ assertThat(files).doesNotContain("ScheduledTaskServiceImpl.java");
223
+ assertThat(files).doesNotContain("ApiExceptionHandler.java");
224
+ assertThat(files).doesNotContain("ScheduledTaskMapper.java");
225
+ assertThat(files).doesNotContain("CasSessionFilter.java", "TrustedPrincipal.java",
226
+ "AgentAuthenticationFilter.java", "LocalCasRealm.java", "CasSessionToken.java",
227
+ "AuthenticatedUser.java");
228
+ assertThat(files).contains(
229
+ "AuthContextHolder.java", "ShiroUser.java", "ResultMsg.java",
230
+ "ShiroConfig.java", "Realm.java", "LoginFormFilter.java",
231
+ "InterceptorConfig.java", "AgentAuthorizationInterceptor.java",
232
+ "AgentPermission.java");
233
+ assertThat(ROOT.resolve("com/epcc/arkweb/schedule/ScheduledTaskScheduler.java")).doesNotExist();
234
+ Path formerSecurityPackage = ROOT.resolve("com/epcc/arkweb/security");
235
+ assertThat(!Files.exists(formerSecurityPackage)
236
+ || Files.walk(formerSecurityPackage).noneMatch(Files::isRegularFile)).isTrue();
237
+ }
238
+
239
+ @Test
240
+ public void webDependsOnTheFacadeAndConsumesDubboReferences() throws Exception {
241
+ String pom = new String(Files.readAllBytes(Paths.get("pom.xml")), StandardCharsets.UTF_8);
242
+ String dubbo = new String(Files.readAllBytes(
243
+ Paths.get("src/main/resources/dubbo-consumer.xml")), StandardCharsets.UTF_8);
244
+
245
+ assertThat(pom).contains("<artifactId>ark-control-facade</artifactId>")
246
+ .doesNotContain("<artifactId>ark-control</artifactId>");
247
+ assertThat(dubbo)
248
+ .contains("id=\"agentProxyService\" interface=\"com.union.control.service.AgentProxyService\" check=\"${dubbo.consumer.check}\" url=\"${dubbo.consumer.direct-url}\" timeout=\"#{T(java.lang.Math).ceil(${AGENT_MAX_RUN_SECONDS:900} * 1000)}\"")
249
+ .contains("url=\"${dubbo.consumer.direct-url}\"")
250
+ .contains("timeout=\"${dubbo.consumer.timeout-ms}\"")
251
+ .contains("retries=\"0\"")
252
+ .doesNotContain("name=\"stream\"")
253
+ .doesNotContain("callback=\"true\"");
254
+
255
+ String config = new String(Files.readAllBytes(
256
+ Paths.get("src/main/resources/application.yml")), StandardCharsets.UTF_8);
257
+ assertThat(config)
258
+ .contains("direct-url: ${DUBBO_DIRECT_URL:}")
259
+ .contains("timeout-ms: ${DUBBO_CONSUMER_TIMEOUT_MS:10000}");
260
+
261
+ String controller = source("com/epcc/arkweb/web/llm/LlmController.java");
262
+ assertThat(controller)
263
+ .contains("pyAppBaseUrl + \"/agent/v1/runs\"")
264
+ .contains("response.getOutputStream().flush()")
265
+ .doesNotContain("gateway.stream(");
266
+ }
267
+
268
+ @Test
269
+ public void webDoesNotLogDubboArgumentsOrStreamChunks() throws Exception {
270
+ String source = Files.walk(ROOT)
271
+ .filter(Files::isRegularFile)
272
+ .map(path -> {
273
+ try {
274
+ return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
275
+ } catch (Exception error) {
276
+ throw new IllegalStateException(error);
277
+ }
278
+ })
279
+ .collect(Collectors.joining("\n"));
280
+
281
+ assertThat(source)
282
+ .doesNotContain("invocation.getArguments()")
283
+ .doesNotContain("rpc.getArguments()")
284
+ .doesNotContain("callback chunk");
285
+ }
286
+
287
+ private static String source(String relative) throws Exception {
288
+ return new String(Files.readAllBytes(ROOT.resolve(relative)), StandardCharsets.UTF_8);
289
+ }
290
+ }
@@ -0,0 +1,32 @@
1
+ package com.epcc.arkweb;
2
+
3
+ import com.epcc.arkweb.model.ShiroUser;
4
+ import org.apache.shiro.mgt.DefaultSecurityManager;
5
+ import org.apache.shiro.subject.SimplePrincipalCollection;
6
+ import org.apache.shiro.subject.Subject;
7
+ import org.apache.shiro.util.ThreadContext;
8
+
9
+ public final class ShiroTestSupport {
10
+ public static final String USER_ID = "user-1";
11
+ public static final String ORG_CODE = "104100000004";
12
+ public static final String ROLE_ID = "ark-role-1";
13
+
14
+ private ShiroTestSupport() {}
15
+
16
+ public static void bindLocalUser() {
17
+ DefaultSecurityManager manager = new DefaultSecurityManager();
18
+ ShiroUser user = new ShiroUser();
19
+ user.setLoginName(USER_ID);
20
+ user.setOrgCode(ORG_CODE);
21
+ user.setRoleId(ROLE_ID);
22
+ Subject subject = new Subject.Builder(manager)
23
+ .principals(new SimplePrincipalCollection(
24
+ user, ShiroTestSupport.class.getName()))
25
+ .authenticated(true).buildSubject();
26
+ subject.getSession().setAttribute("wl_session_user_key", user);
27
+ ThreadContext.bind(manager);
28
+ ThreadContext.bind(subject);
29
+ }
30
+
31
+ public static void clear() { ThreadContext.remove(); }
32
+ }
@@ -0,0 +1,121 @@
1
+ package com.epcc.arkweb.config;
2
+
3
+ import com.epcc.arkweb.helper.AuthenticatedRequest;
4
+ import com.epcc.arkweb.mock.ArkAuthServiceMock;
5
+ import com.fasterxml.jackson.databind.ObjectMapper;
6
+ import org.apache.shiro.SecurityUtils;
7
+ import org.apache.shiro.authc.UsernamePasswordToken;
8
+ import org.apache.shiro.mgt.SecurityManager;
9
+ import org.apache.shiro.subject.Subject;
10
+ import org.apache.shiro.util.ThreadContext;
11
+ import org.junit.After;
12
+ import org.junit.Test;
13
+ import org.springframework.context.annotation.AnnotationConfigApplicationContext;
14
+ import org.springframework.context.annotation.Bean;
15
+ import org.springframework.context.annotation.Configuration;
16
+ import org.springframework.context.annotation.Import;
17
+ import org.springframework.core.env.MapPropertySource;
18
+ import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
19
+ import org.springframework.mock.web.MockFilterChain;
20
+ import org.springframework.mock.web.MockHttpServletRequest;
21
+ import org.springframework.mock.web.MockHttpServletResponse;
22
+
23
+ import javax.servlet.Filter;
24
+
25
+ import java.util.Collections;
26
+
27
+ import static org.assertj.core.api.Assertions.assertThat;
28
+
29
+ public class LocalShiroIntegrationTest {
30
+ @After
31
+ public void clearShiro() {
32
+ ThreadContext.remove();
33
+ SecurityUtils.setSecurityManager(null);
34
+ }
35
+
36
+ @Test
37
+ public void authenticatedRequestUsesTheShiroSubjectAndPermissions() {
38
+ AnnotationConfigApplicationContext context = context("local-auth-mock");
39
+ try {
40
+ SecurityManager manager = context.getBean(SecurityManager.class);
41
+ SecurityUtils.setSecurityManager(manager);
42
+ Subject subject = new Subject.Builder(manager).buildSubject();
43
+ subject.login(new UsernamePasswordToken("user-1", "local-only"));
44
+ subject.execute(() -> {
45
+ assertThat(context.getBean(AuthenticatedRequest.class).actor().getRoleId())
46
+ .isEqualTo("role-1");
47
+ assertThat(SecurityUtils.getSubject().isPermitted("/assistantManager/page"))
48
+ .isTrue();
49
+ });
50
+ } finally {
51
+ context.close();
52
+ }
53
+ }
54
+
55
+ @Test
56
+ public void localRequestAuthenticatesWithoutSubmittingTheLoginForm() throws Exception {
57
+ AnnotationConfigApplicationContext context = context("local-auth-mock");
58
+ try {
59
+ SecurityManager manager = context.getBean(SecurityManager.class);
60
+ SecurityUtils.setSecurityManager(manager);
61
+ context.getBean("localAuthFilter", Filter.class).doFilter(
62
+ new MockHttpServletRequest(), new MockHttpServletResponse(),
63
+ new MockFilterChain());
64
+ assertThat(SecurityUtils.getSubject().isAuthenticated()).isTrue();
65
+ assertThat(SecurityUtils.getSubject().isPermitted("/assistantManager/page")).isTrue();
66
+ } finally {
67
+ context.close();
68
+ }
69
+ }
70
+
71
+ @Test
72
+ public void scheduledRequestDoesNotCreateAShiroLogin() throws Exception {
73
+ AnnotationConfigApplicationContext context = context("local-auth-mock");
74
+ try {
75
+ SecurityManager manager = context.getBean(SecurityManager.class);
76
+ SecurityUtils.setSecurityManager(manager);
77
+ MockHttpServletRequest request = new MockHttpServletRequest();
78
+ request.addHeader("Authorization",
79
+ "Scheduled AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
80
+ context.getBean("localAuthFilter", Filter.class).doFilter(
81
+ request, new MockHttpServletResponse(), new MockFilterChain());
82
+ assertThat(SecurityUtils.getSubject().isAuthenticated()).isFalse();
83
+ } finally {
84
+ context.close();
85
+ }
86
+ }
87
+
88
+ @Test
89
+ public void localShiroDoesNotRegisterWithoutTheExplicitProfile() {
90
+ AnnotationConfigApplicationContext context = context();
91
+ try {
92
+ assertThat(context.getBeansOfType(ShiroConfig.class)).isEmpty();
93
+ assertThat(context.getBeansOfType(ArkAuthServiceMock.class)).isEmpty();
94
+ } finally {
95
+ context.close();
96
+ }
97
+ }
98
+
99
+ private static AnnotationConfigApplicationContext context(String... profiles) {
100
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
101
+ context.getEnvironment().setActiveProfiles(profiles);
102
+ if (profiles.length > 0)
103
+ context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
104
+ "test", Collections.<String, Object>singletonMap("arkshiro.sso", "false")));
105
+ context.register(TestConfiguration.class);
106
+ context.refresh();
107
+ return context;
108
+ }
109
+
110
+ @Configuration
111
+ @Import({ShiroConfig.class, ArkAuthServiceMock.class})
112
+ public static class TestConfiguration {
113
+ @Bean public static PropertySourcesPlaceholderConfigurer placeholders() {
114
+ return new PropertySourcesPlaceholderConfigurer();
115
+ }
116
+ @Bean public ObjectMapper objectMapper() { return new ObjectMapper(); }
117
+ @Bean public AuthenticatedRequest authenticatedRequest(ObjectMapper json) {
118
+ return new AuthenticatedRequest(json);
119
+ }
120
+ }
121
+ }
@@ -0,0 +1,131 @@
1
+ package com.epcc.arkweb.helper;
2
+
3
+ import com.epcc.arkweb.ShiroTestSupport;
4
+ import com.fasterxml.jackson.core.type.TypeReference;
5
+ import com.fasterxml.jackson.databind.ObjectMapper;
6
+ import com.epcc.arkweb.web.llm.AgentAuthorizationInterceptor;
7
+ import com.union.control.service.ScheduledTaskService;
8
+ import org.junit.After;
9
+ import org.junit.Before;
10
+ import org.junit.Test;
11
+ import org.springframework.mock.web.MockHttpServletRequest;
12
+ import org.springframework.mock.web.MockHttpServletResponse;
13
+ import org.springframework.web.context.request.RequestContextHolder;
14
+ import org.springframework.web.context.request.ServletRequestAttributes;
15
+
16
+ import java.util.LinkedHashMap;
17
+ import java.util.Map;
18
+
19
+ import static org.assertj.core.api.Assertions.assertThat;
20
+ import static org.mockito.Mockito.mock;
21
+ import static org.mockito.Mockito.when;
22
+
23
+ public class AuthenticatedRequestTest {
24
+ private final ObjectMapper json = new ObjectMapper();
25
+
26
+ @Before
27
+ public void setUp() { ShiroTestSupport.bindLocalUser(); }
28
+
29
+ @After
30
+ public void tearDown() {
31
+ RequestContextHolder.resetRequestAttributes();
32
+ ShiroTestSupport.clear();
33
+ }
34
+
35
+ @Test
36
+ public void authenticatedIdentityOverridesClientSuppliedIdentity() throws Exception {
37
+ Map<String, Object> payload = new LinkedHashMap<>();
38
+ payload.put("userId", "attacker");
39
+ payload.put("orgCode", "attacker-org");
40
+ payload.put("roleId", "attacker-role");
41
+ payload.put("conversationId", "thread-1");
42
+
43
+ String input = new AuthenticatedRequest(json).json(payload);
44
+ Map<String, Object> parsed = json.readValue(
45
+ input, new TypeReference<Map<String, Object>>() {});
46
+
47
+ assertThat(parsed)
48
+ .containsEntry("userId", ShiroTestSupport.USER_ID)
49
+ .containsEntry("orgCode", ShiroTestSupport.ORG_CODE)
50
+ .containsEntry("roleId", ShiroTestSupport.ROLE_ID)
51
+ .containsEntry("conversationId", "thread-1");
52
+ }
53
+
54
+ @Test
55
+ public void removesClientIdentityBeforeForwardingPayload() throws Exception {
56
+ byte[] input = "{\"userId\":\"attacker\",\"orgCode\":\"x\",\"prompt\":\"hello\"}"
57
+ .getBytes("UTF-8");
58
+
59
+ Map<String, Object> parsed = json.readValue(
60
+ new AuthenticatedRequest(json).clientPayload(input),
61
+ new TypeReference<Map<String, Object>>() {});
62
+
63
+ assertThat(parsed).containsEntry("prompt", "hello")
64
+ .doesNotContainKeys("userId", "orgCode", "roleId");
65
+ }
66
+
67
+ @Test
68
+ public void scheduledContextOverridesClientIdentityWithoutUsingShiro() throws Exception {
69
+ Map<String, Object> context = new LinkedHashMap<>();
70
+ context.put("runId", 7L);
71
+ context.put("taskId", 8L);
72
+ context.put("userId", "scheduled-user");
73
+ context.put("orgCode", "scheduled-org");
74
+ context.put("roleId", "scheduled-role");
75
+ context.put("prompt", "prompt");
76
+ context.put("scheduledAt", "2026-09-02T01:00:00.000Z");
77
+ context.put("timezone", "UTC");
78
+ context.put("expiresAt", "2030-09-02T02:00:00.000Z");
79
+ MockHttpServletRequest servletRequest = new MockHttpServletRequest();
80
+ ScheduledTaskService tasks = mock(ScheduledTaskService.class);
81
+ when(tasks.resolveScheduledToken(
82
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")).thenReturn(context);
83
+ AgentAuthorizationInterceptor authorization = new AgentAuthorizationInterceptor(tasks);
84
+ authorization.setArkAuthService(new FakeArkAuthService());
85
+ MockHttpServletResponse servletResponse = new MockHttpServletResponse();
86
+ servletRequest.addHeader(AgentAuthorizationInterceptor.TRUSTED_CONTEXT_HEADER,
87
+ "Scheduled AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
88
+ RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(servletRequest));
89
+ authorization.preHandle(servletRequest, servletResponse, scheduledHandler());
90
+
91
+ Map<String, Object> payload = new LinkedHashMap<>();
92
+ payload.put("userId", "attacker");
93
+ payload.put("orgCode", "attacker-org");
94
+ payload.put("roleId", "attacker-role");
95
+
96
+ Map<String, Object> parsed = json.readValue(
97
+ new AuthenticatedRequest(json).json(payload),
98
+ new TypeReference<Map<String, Object>>() {});
99
+
100
+ assertThat(parsed).containsEntry("userId", "scheduled-user")
101
+ .containsEntry("orgCode", "scheduled-org")
102
+ .containsEntry("roleId", "scheduled-role");
103
+ }
104
+
105
+ private static org.springframework.web.method.HandlerMethod scheduledHandler() throws Exception {
106
+ return new org.springframework.web.method.HandlerMethod(new ScheduledProbe(),
107
+ ScheduledProbe.class.getMethod("tool"));
108
+ }
109
+
110
+ public static final class ScheduledProbe {
111
+ @com.epcc.arkweb.web.llm.AgentPermission("/assistantManager/page")
112
+ public void tool() {}
113
+ }
114
+
115
+ public static final class FakeArkAuthService {
116
+ public FakeResult queryResource(String roleId, String userName, String traceNo) {
117
+ return new FakeResult(java.util.Collections.singletonList(new FakeResource()));
118
+ }
119
+ }
120
+
121
+ public static final class FakeResult {
122
+ private final Object result;
123
+ FakeResult(Object result) { this.result = result; }
124
+ public boolean isSuccess() { return true; }
125
+ public Object getResult() { return result; }
126
+ }
127
+
128
+ public static final class FakeResource {
129
+ public String getResourceUrl() { return "/assistantManager/page"; }
130
+ }
131
+ }
@@ -0,0 +1,17 @@
1
+ package com.epcc.arkweb.mock;
2
+
3
+ import org.junit.Test;
4
+
5
+ import static org.assertj.core.api.Assertions.assertThat;
6
+
7
+ public class LocalArkAuthServiceMockTest {
8
+ @Test
9
+ public void grantsOnlyTheConfiguredRole() {
10
+ ArkAuthServiceMock mock = new ArkAuthServiceMock(
11
+ "user-1", "local-only", "104100000004", "role-1");
12
+
13
+ assertThat(mock.queryResource("role-1", "user-1", "trace").getResult())
14
+ .extracting("resourceUrl").containsExactly("/assistantManager/page");
15
+ assertThat(mock.queryResource("role-2", "user-1", "trace").getResult()).isEmpty();
16
+ }
17
+ }