fix: 收口管理端页面权限与工作流运行授权
- 页面选项接口改用所属页面权限并返回最小数据视图 - 统一校验工作流引用、租户、状态与定时任务执行主体 - 补充聊天记录权限迁移和权限隔离回归测试
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
package tech.easyflow.job.service;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.easyagents.flow.core.chain.runtime.ChainExecutor;
|
||||
import com.mybatisflex.core.tenant.TenantManager;
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.ai.service.WorkflowUsageAuthorizationService;
|
||||
import tech.easyflow.common.constant.Constants;
|
||||
import tech.easyflow.common.constant.enums.EnumDataStatus;
|
||||
import tech.easyflow.common.constant.enums.EnumJobStatus;
|
||||
import tech.easyflow.common.entity.LoginAccount;
|
||||
import tech.easyflow.job.entity.SysJob;
|
||||
import tech.easyflow.job.job.JobConstant;
|
||||
import tech.easyflow.job.support.SysJobWorkflowReferenceSupport;
|
||||
import tech.easyflow.system.entity.SysAccount;
|
||||
import tech.easyflow.system.service.SysAccountService;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 工作流定时任务执行服务。
|
||||
*
|
||||
* <p>每次触发都重新加载任务、账号和工作流,并按服务端记录恢复执行主体及重新授权。</p>
|
||||
*/
|
||||
@Service
|
||||
public class WorkflowJobExecutionService {
|
||||
|
||||
/** 定时任务服务。 */
|
||||
private final SysJobService sysJobService;
|
||||
|
||||
/** 系统账号服务。 */
|
||||
private final SysAccountService sysAccountService;
|
||||
|
||||
/** 工作流使用权限校验服务。 */
|
||||
private final WorkflowUsageAuthorizationService workflowUsageAuthorizationService;
|
||||
|
||||
/** 工作流执行器。 */
|
||||
private final ChainExecutor chainExecutor;
|
||||
|
||||
/**
|
||||
* 创建工作流定时任务执行服务。
|
||||
*
|
||||
* @param sysJobService 定时任务服务
|
||||
* @param sysAccountService 系统账号服务
|
||||
* @param workflowUsageAuthorizationService 工作流使用权限校验服务
|
||||
* @param chainExecutor 工作流执行器
|
||||
*/
|
||||
public WorkflowJobExecutionService(
|
||||
SysJobService sysJobService,
|
||||
SysAccountService sysAccountService,
|
||||
WorkflowUsageAuthorizationService workflowUsageAuthorizationService,
|
||||
ChainExecutor chainExecutor) {
|
||||
this.sysJobService = sysJobService;
|
||||
this.sysAccountService = sysAccountService;
|
||||
this.workflowUsageAuthorizationService = workflowUsageAuthorizationService;
|
||||
this.chainExecutor = chainExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用当前数据库状态执行工作流定时任务。
|
||||
*
|
||||
* @param scheduledJob Quartz 中保存的任务快照
|
||||
* @return 工作流执行结果
|
||||
* @throws IllegalStateException 任务、账号或租户状态非法时抛出
|
||||
*/
|
||||
public Object execute(SysJob scheduledJob) {
|
||||
if (scheduledJob == null || scheduledJob.getId() == null) {
|
||||
throw new IllegalStateException("定时任务不存在或缺少ID");
|
||||
}
|
||||
return TenantManager.withoutTenantCondition(
|
||||
() -> executeWithoutTenantCondition(
|
||||
scheduledJob.getId(),
|
||||
scheduledJob.getTenantId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 在已关闭 ORM 租户条件的作用域中执行任务,并显式完成租户边界校验。
|
||||
*
|
||||
* @param jobId 定时任务 ID
|
||||
* @param scheduledTenantId Quartz 任务快照中的租户 ID
|
||||
* @return 工作流执行结果
|
||||
* @throws IllegalStateException 任务、账号或租户状态非法时抛出
|
||||
*/
|
||||
private Object executeWithoutTenantCondition(
|
||||
BigInteger jobId,
|
||||
BigInteger scheduledTenantId) {
|
||||
SysJob job = sysJobService.getById(jobId);
|
||||
if (job == null) {
|
||||
throw new IllegalStateException("定时任务不存在或已删除,id=" + jobId);
|
||||
}
|
||||
if (scheduledTenantId == null
|
||||
|| job.getTenantId() == null
|
||||
|| !Objects.equals(scheduledTenantId, job.getTenantId())) {
|
||||
throw new IllegalStateException("定时任务租户信息不一致,id=" + jobId);
|
||||
}
|
||||
if (!Integer.valueOf(EnumJobStatus.RUNNING.getCode()).equals(job.getStatus())) {
|
||||
throw new IllegalStateException("定时任务未处于运行状态,id=" + jobId);
|
||||
}
|
||||
if (!SysJobWorkflowReferenceSupport.isWorkflowJob(job)) {
|
||||
throw new IllegalStateException("定时任务类型已变更,id=" + jobId);
|
||||
}
|
||||
|
||||
SysAccount account = requireAvailableOwner(job);
|
||||
LoginAccount loginAccount = new LoginAccount();
|
||||
BeanUtil.copyProperties(account, loginAccount);
|
||||
BigInteger workflowId = SysJobWorkflowReferenceSupport.requireWorkflowId(job);
|
||||
workflowUsageAuthorizationService.requireUsableWorkflow(
|
||||
workflowId,
|
||||
loginAccount,
|
||||
"定时任务关联的工作流不存在、已禁用或无权运行");
|
||||
|
||||
JSONObject workflowParams = resolveWorkflowParams(job.getJobParams());
|
||||
workflowParams.put(Constants.LOGIN_USER_KEY, loginAccount);
|
||||
return chainExecutor.execute(workflowId.toString(), workflowParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务创建账号并校验账号仍可用于执行任务。
|
||||
*
|
||||
* @param job 当前数据库中的定时任务
|
||||
* @return 可用的任务创建账号
|
||||
* @throws IllegalStateException 创建账号缺失、禁用或跨租户时抛出
|
||||
*/
|
||||
private SysAccount requireAvailableOwner(SysJob job) {
|
||||
BigInteger accountId = job.getCreatedBy();
|
||||
if (accountId == null) {
|
||||
throw new IllegalStateException("定时任务缺少服务端归属账号,id=" + job.getId());
|
||||
}
|
||||
SysAccount account = sysAccountService.getById(accountId);
|
||||
if (account == null) {
|
||||
throw new IllegalStateException("定时任务归属账号不存在,id=" + accountId);
|
||||
}
|
||||
if (!EnumDataStatus.AVAILABLE.getCode().equals(account.getStatus())) {
|
||||
throw new IllegalStateException("定时任务归属账号未启用,id=" + accountId);
|
||||
}
|
||||
if (!Objects.equals(job.getTenantId(), account.getTenantId())) {
|
||||
throw new IllegalStateException("定时任务与归属账号租户不一致,id=" + job.getId());
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析工作流运行参数并返回可写对象。
|
||||
*
|
||||
* @param jobParams 定时任务参数
|
||||
* @return 工作流运行参数
|
||||
*/
|
||||
private JSONObject resolveWorkflowParams(Map<String, Object> jobParams) {
|
||||
if (jobParams == null) {
|
||||
return new JSONObject();
|
||||
}
|
||||
JSONObject params = new JSONObject(jobParams)
|
||||
.getJSONObject(JobConstant.WORKFLOW_PARAMS_KEY);
|
||||
return params == null ? new JSONObject() : new JSONObject(params);
|
||||
}
|
||||
}
|
||||
@@ -2,24 +2,15 @@ package tech.easyflow.job.util;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.mybatisflex.core.tenant.TenantManager;
|
||||
import com.easyagents.flow.core.chain.ChainDefinition;
|
||||
import com.easyagents.flow.core.chain.runtime.ChainExecutor;
|
||||
import org.quartz.JobKey;
|
||||
import org.quartz.TriggerKey;
|
||||
import tech.easyflow.common.constant.Constants;
|
||||
import tech.easyflow.common.constant.enums.EnumJobType;
|
||||
import tech.easyflow.common.satoken.util.SaTokenUtil;
|
||||
import tech.easyflow.common.util.SpringContextUtil;
|
||||
import tech.easyflow.job.entity.SysJob;
|
||||
import tech.easyflow.job.job.JobConstant;
|
||||
import tech.easyflow.job.support.SysJobWorkflowReferenceSupport;
|
||||
import tech.easyflow.system.entity.SysAccount;
|
||||
import tech.easyflow.system.service.SysAccountService;
|
||||
import tech.easyflow.job.service.WorkflowJobExecutionService;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -68,34 +59,16 @@ public class JobUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过任务模块的受控执行服务运行工作流。
|
||||
*
|
||||
* @param job Quartz 中保存的任务快照
|
||||
* @return 工作流执行结果
|
||||
*/
|
||||
public static Object execWorkFlow(SysJob job) {
|
||||
Map<String, Object> jobParams = job.getJobParams();
|
||||
JSONObject obj = new JSONObject(jobParams);
|
||||
BigInteger workflowId = SysJobWorkflowReferenceSupport.requireWorkflowId(job);
|
||||
JSONObject params = obj.getJSONObject(JobConstant.WORKFLOW_PARAMS_KEY);
|
||||
|
||||
ChainExecutor executor = SpringContextUtil.getBean(ChainExecutor.class);
|
||||
Object accountId = obj.get(JobConstant.ACCOUNT_ID);
|
||||
SysAccountService accountService = SpringContextUtil.getBean(SysAccountService.class);
|
||||
|
||||
try {
|
||||
TenantManager.ignoreTenantCondition();
|
||||
|
||||
ChainDefinition chain = executor.getDefinitionRepository().getChainDefinitionById(workflowId.toString());
|
||||
if (chain == null) {
|
||||
throw new IllegalStateException("定时任务关联的工作流不存在或已删除,id=" + workflowId);
|
||||
}
|
||||
if (accountId != null) {
|
||||
// 设置的归属者
|
||||
SysAccount account = accountService.getById(accountId.toString());
|
||||
if (account != null) {
|
||||
params.put(Constants.LOGIN_USER_KEY, SaTokenUtil.getLoginAccount());
|
||||
}
|
||||
}
|
||||
return executor.execute(workflowId.toString(), params);
|
||||
} finally {
|
||||
TenantManager.restoreTenantCondition();
|
||||
}
|
||||
WorkflowJobExecutionService executionService =
|
||||
SpringContextUtil.getBean(WorkflowJobExecutionService.class);
|
||||
return executionService.execute(job);
|
||||
}
|
||||
|
||||
public static Object execute(SysJob job) {
|
||||
|
||||
Reference in New Issue
Block a user