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,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
|
+
}
|