fix: 收口管理端页面权限与工作流运行授权
- 页面选项接口改用所属页面权限并返回最小数据视图 - 统一校验工作流引用、租户、状态与定时任务执行主体 - 补充聊天记录权限迁移和权限隔离回归测试
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
package tech.easyflow.admin.service.ai;
|
||||
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.easyagents.flow.core.parser.ChainParser;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
import tech.easyflow.ai.easyagentsflow.service.WorkflowDatacenterContentService;
|
||||
import tech.easyflow.ai.entity.Model;
|
||||
import tech.easyflow.ai.entity.Workflow;
|
||||
import tech.easyflow.ai.plugin.workflow.snapshot.WorkflowPluginSnapshotResolver;
|
||||
import tech.easyflow.ai.service.DocumentCollectionService;
|
||||
import tech.easyflow.ai.service.ModelService;
|
||||
import tech.easyflow.ai.service.PluginItemService;
|
||||
import tech.easyflow.ai.service.PluginService;
|
||||
import tech.easyflow.ai.service.PluginVisibilityService;
|
||||
import tech.easyflow.ai.service.WorkflowService;
|
||||
import tech.easyflow.ai.service.WorkflowUsageAuthorizationService;
|
||||
import tech.easyflow.common.entity.LoginAccount;
|
||||
import tech.easyflow.common.satoken.util.SaTokenUtil;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.datacenter.execution.service.DatacenterDatasetQueryService;
|
||||
import tech.easyflow.datacenter.meta.entity.DatacenterSource;
|
||||
import tech.easyflow.datacenter.meta.service.DatacenterDatasetRegistryService;
|
||||
import tech.easyflow.datacenter.meta.service.DatacenterSourceService;
|
||||
import tech.easyflow.system.service.ResourceAccessService;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link WorkflowDesignerOptionService} 工作流引用权限测试。
|
||||
*/
|
||||
public class WorkflowDesignerOptionServiceTest {
|
||||
|
||||
/**
|
||||
* 验证客户端提交候选列表之外的模型 ID 时服务端拒绝保存。
|
||||
*/
|
||||
@Test
|
||||
public void shouldRejectModelOutsideSelectableOptions() {
|
||||
ModelService modelService = mock(ModelService.class);
|
||||
DocumentCollectionService knowledgeService = mock(DocumentCollectionService.class);
|
||||
when(modelService.listByIds(any())).thenReturn(List.of());
|
||||
when(knowledgeService.list(any(QueryWrapper.class))).thenReturn(List.of());
|
||||
WorkflowDesignerOptionService service = createService(
|
||||
modelService, knowledgeService, mock(DatacenterSourceService.class));
|
||||
|
||||
try (MockedStatic<SaTokenUtil> saToken = mockStatic(SaTokenUtil.class)) {
|
||||
saToken.when(SaTokenUtil::getLoginAccount).thenReturn(loginAccount());
|
||||
|
||||
BusinessException exception = Assert.expectThrows(
|
||||
BusinessException.class,
|
||||
() -> service.assertContentReferences(
|
||||
"{\"nodes\":[{\"type\":\"llmNode\",\"data\":{\"llmId\":\"99\"}}]}")
|
||||
);
|
||||
|
||||
Assert.assertTrue(exception.getMessage().contains("模型"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证工作流数据节点不能引用其他租户的数据源。
|
||||
*/
|
||||
@Test
|
||||
public void shouldRejectCrossTenantDataSource() {
|
||||
ModelService modelService = mock(ModelService.class);
|
||||
DocumentCollectionService knowledgeService = mock(DocumentCollectionService.class);
|
||||
DatacenterSourceService sourceService = mock(DatacenterSourceService.class);
|
||||
when(modelService.listSelectableModels(any(Model.class), eq(false), eq("id"), eq("desc")))
|
||||
.thenReturn(List.of());
|
||||
when(knowledgeService.list(any(QueryWrapper.class))).thenReturn(List.of());
|
||||
DatacenterSource source = new DatacenterSource();
|
||||
source.setId(BigInteger.valueOf(9));
|
||||
source.setTenantId(BigInteger.valueOf(200));
|
||||
when(sourceService.getById(BigInteger.valueOf(9))).thenReturn(source);
|
||||
WorkflowDesignerOptionService service = createService(
|
||||
modelService, knowledgeService, sourceService);
|
||||
|
||||
try (MockedStatic<SaTokenUtil> saToken = mockStatic(SaTokenUtil.class)) {
|
||||
saToken.when(SaTokenUtil::getLoginAccount).thenReturn(loginAccount());
|
||||
|
||||
BusinessException exception = Assert.expectThrows(
|
||||
BusinessException.class,
|
||||
() -> service.assertContentReferences("""
|
||||
{"nodes":[{"type":"search-dataset-node","data":{
|
||||
"datasetRef":{"sourceId":"9"}
|
||||
}}]}
|
||||
""")
|
||||
);
|
||||
|
||||
Assert.assertTrue(exception.getMessage().contains("数据源"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证子流程节点配置拒绝读取其他租户的工作流。
|
||||
*/
|
||||
@Test
|
||||
public void shouldRejectCrossTenantChildWorkflow() {
|
||||
WorkflowService workflowService = mock(WorkflowService.class);
|
||||
Workflow workflow = new Workflow();
|
||||
workflow.setId(BigInteger.valueOf(19));
|
||||
workflow.setTenantId(BigInteger.valueOf(200));
|
||||
when(workflowService.getById(BigInteger.valueOf(19))).thenReturn(workflow);
|
||||
WorkflowDesignerOptionService service = createService(
|
||||
mock(ModelService.class),
|
||||
mock(DocumentCollectionService.class),
|
||||
mock(DatacenterSourceService.class),
|
||||
workflowService
|
||||
);
|
||||
|
||||
try (MockedStatic<SaTokenUtil> saToken = mockStatic(SaTokenUtil.class)) {
|
||||
saToken.when(SaTokenUtil::getLoginAccount).thenReturn(loginAccount());
|
||||
|
||||
BusinessException exception = Assert.expectThrows(
|
||||
BusinessException.class,
|
||||
() -> service.getChildWorkflowNodeData(
|
||||
BigInteger.valueOf(10),
|
||||
BigInteger.valueOf(19))
|
||||
);
|
||||
|
||||
Assert.assertTrue(exception.getMessage().contains("子流程"));
|
||||
}
|
||||
}
|
||||
|
||||
private WorkflowDesignerOptionService createService(
|
||||
ModelService modelService,
|
||||
DocumentCollectionService knowledgeService,
|
||||
DatacenterSourceService sourceService) {
|
||||
return createService(modelService, knowledgeService, sourceService, mock(WorkflowService.class));
|
||||
}
|
||||
|
||||
private WorkflowDesignerOptionService createService(
|
||||
ModelService modelService,
|
||||
DocumentCollectionService knowledgeService,
|
||||
DatacenterSourceService sourceService,
|
||||
WorkflowService workflowService) {
|
||||
ResourceAccessService resourceAccessService = mock(ResourceAccessService.class);
|
||||
return new WorkflowDesignerOptionService(
|
||||
modelService,
|
||||
knowledgeService,
|
||||
mock(PluginService.class),
|
||||
mock(PluginItemService.class),
|
||||
mock(PluginVisibilityService.class),
|
||||
workflowService,
|
||||
new WorkflowUsageAuthorizationService(workflowService, resourceAccessService),
|
||||
mock(WorkflowPluginSnapshotResolver.class),
|
||||
mock(ChainParser.class),
|
||||
mock(WorkflowDatacenterContentService.class),
|
||||
resourceAccessService,
|
||||
sourceService,
|
||||
mock(DatacenterDatasetRegistryService.class),
|
||||
mock(DatacenterDatasetQueryService.class)
|
||||
);
|
||||
}
|
||||
|
||||
private LoginAccount loginAccount() {
|
||||
LoginAccount account = new LoginAccount();
|
||||
account.setId(BigInteger.ONE);
|
||||
account.setTenantId(BigInteger.valueOf(100));
|
||||
return account;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user