From 3b4b431e2a3be5df1da7780099fa61a26b782c46 Mon Sep 17 00:00:00 2001 From: luoshangjie <75019445+18216499322@users.noreply.github.com> Date: Wed, 26 Jun 2024 11:36:52 +0800 Subject: [PATCH] [Improve] Improve the implementation logic for task operation permission check (#3610) Co-authored-by: luoshangjie Co-authored-by: 18216499322 <18216499322@users.noreply.github.com> --- .../dinky/aop/TaskOperationCheckAspect.java | 137 ------------------ .../aop/TaskOperationPermissionAspect.java | 126 ++++++++++++++++ .../dinky/controller/CatalogueController.java | 14 +- .../org/dinky/controller/TaskController.java | 31 ++-- .../org/dinky/data/dto/CatalogueTaskDTO.java | 2 + .../main/java/org/dinky/data/dto/TaskDTO.java | 2 + .../data/dto/TaskRollbackVersionDTO.java | 3 + .../java/org/dinky/data/dto/TaskSaveDTO.java | 2 + .../java/org/dinky/data/model/Catalogue.java | 2 + .../java/org/dinky/service/TaskService.java | 8 + .../service/catalogue/CatalogueService.java | 8 + .../catalogue/impl/CatalogueServiceImpl.java | 10 ++ .../dinky/service/impl/TaskServiceImpl.java | 24 +++ .../dinky/data/annotations/CatalogueId.java | 31 ++++ .../data/annotations/CheckTaskOwner.java | 6 +- .../org/dinky/data/annotations/TaskId.java | 31 ++++ 16 files changed, 278 insertions(+), 159 deletions(-) delete mode 100644 dinky-admin/src/main/java/org/dinky/aop/TaskOperationCheckAspect.java create mode 100644 dinky-admin/src/main/java/org/dinky/aop/TaskOperationPermissionAspect.java create mode 100644 dinky-common/src/main/java/org/dinky/data/annotations/CatalogueId.java create mode 100644 dinky-common/src/main/java/org/dinky/data/annotations/TaskId.java diff --git a/dinky-admin/src/main/java/org/dinky/aop/TaskOperationCheckAspect.java b/dinky-admin/src/main/java/org/dinky/aop/TaskOperationCheckAspect.java deleted file mode 100644 index 9c10a592a3..0000000000 --- a/dinky-admin/src/main/java/org/dinky/aop/TaskOperationCheckAspect.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.dinky.aop; - -import org.dinky.data.annotations.CheckTaskOwner; -import org.dinky.data.constant.BaseConstant; -import org.dinky.data.dto.CatalogueTaskDTO; -import org.dinky.data.dto.TaskDTO; -import org.dinky.data.dto.TaskRollbackVersionDTO; -import org.dinky.data.dto.TaskSaveDTO; -import org.dinky.data.enums.Status; -import org.dinky.data.enums.TaskOwnerLockStrategyEnum; -import org.dinky.data.exception.BusException; -import org.dinky.data.model.Catalogue; -import org.dinky.data.model.SystemConfiguration; -import org.dinky.data.model.Task; -import org.dinky.service.TaskService; -import org.dinky.service.catalogue.CatalogueService; - -import java.util.List; -import java.util.Objects; - -import javax.annotation.Resource; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.springframework.stereotype.Component; - -import cn.dev33.satoken.stp.StpUtil; -import cn.hutool.core.bean.BeanUtil; -import lombok.extern.slf4j.Slf4j; - -@Aspect -@Slf4j -@Component -public class TaskOperationCheckAspect { - - @Resource - private TaskService taskService; - - @Resource - private CatalogueService catalogueService; - - /** - * Check whether the user has the permission to perform the task. - * - * @param joinPoint - * @param checkTaskOwner - * @return - * @throws Throwable - */ - @Around(value = "@annotation(checkTaskOwner)") - public Object processAround(ProceedingJoinPoint joinPoint, CheckTaskOwner checkTaskOwner) throws Throwable { - if (!TaskOwnerLockStrategyEnum.ALL.equals( - SystemConfiguration.getInstances().getTaskOwnerLockStrategy()) - && BaseConstant.ADMIN_ID != StpUtil.getLoginIdAsInt()) { - Class serviceType = checkTaskOwner.serviceType(); - Integer id = getId(joinPoint); - if (Objects.nonNull(id)) { - Task task = null; - if (TaskService.class.equals(serviceType)) { - task = taskService.getById(id); - } else if (CatalogueService.class.equals(serviceType)) { - Catalogue catalogue = catalogueService.getById(id); - if (BeanUtil.isNotEmpty(catalogue) && catalogue.getIsLeaf()) { - task = taskService.getById(catalogue.getTaskId()); - } - } - if (Objects.nonNull(task)) { - if (!hasPermission(task.getFirstLevelOwner(), task.getSecondLevelOwners())) { - throw new BusException(Status.TASK_NOT_OPERATE_PERMISSION); - } - } - } - } - - Object result; - try { - result = joinPoint.proceed(); - } catch (Throwable e) { - throw e; - } - return result; - } - - private Boolean hasPermission(Integer firstLevelOwner, List secondLevelOwners) { - boolean isFirstLevelOwner = firstLevelOwner != null && firstLevelOwner == StpUtil.getLoginIdAsInt(); - if (TaskOwnerLockStrategyEnum.OWNER.equals( - SystemConfiguration.getInstances().getTaskOwnerLockStrategy())) { - return isFirstLevelOwner; - } else if (TaskOwnerLockStrategyEnum.OWNER_AND_MAINTAINER.equals( - SystemConfiguration.getInstances().getTaskOwnerLockStrategy())) { - return isFirstLevelOwner - || (secondLevelOwners != null && secondLevelOwners.contains(StpUtil.getLoginIdAsInt())); - } - return true; - } - - private Integer getId(ProceedingJoinPoint joinPoint) { - Integer id = null; - - Object[] args = joinPoint.getArgs(); - if (args[0] instanceof Integer) { - id = (Integer) args[0]; - } else if (args[0] instanceof TaskDTO) { - id = ((TaskDTO) args[0]).getId(); - } else if (args[0] instanceof TaskSaveDTO) { - id = ((TaskSaveDTO) args[0]).getId(); - } else if (args[0] instanceof TaskRollbackVersionDTO) { - id = ((TaskRollbackVersionDTO) args[0]).getTaskId(); - } else if (args[0] instanceof CatalogueTaskDTO) { - id = ((CatalogueTaskDTO) args[0]).getTaskId(); - } else if (args[0] instanceof Catalogue) { - id = ((Catalogue) args[0]).getTaskId(); - } - - return id; - } -} diff --git a/dinky-admin/src/main/java/org/dinky/aop/TaskOperationPermissionAspect.java b/dinky-admin/src/main/java/org/dinky/aop/TaskOperationPermissionAspect.java new file mode 100644 index 0000000000..76411b3851 --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/aop/TaskOperationPermissionAspect.java @@ -0,0 +1,126 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.dinky.aop; + +import org.dinky.data.annotations.CheckTaskOwner; +import org.dinky.data.constant.BaseConstant; +import org.dinky.data.enums.Status; +import org.dinky.data.enums.TaskOwnerLockStrategyEnum; +import org.dinky.data.exception.BusException; +import org.dinky.data.model.SystemConfiguration; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Objects; + +import javax.annotation.Resource; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +import cn.dev33.satoken.stp.StpUtil; +import lombok.extern.slf4j.Slf4j; + +@Aspect +@Slf4j +@Component +public class TaskOperationPermissionAspect { + + @Resource + private ApplicationContext applicationContext; + + /** + * Check whether the user has the permission to perform the task. + * + * @param joinPoint + * @param checkTaskOwner + * @return + * @throws Throwable + */ + @Around(value = "@annotation(checkTaskOwner)") + public Object processAround(ProceedingJoinPoint joinPoint, CheckTaskOwner checkTaskOwner) throws Throwable { + if (!TaskOwnerLockStrategyEnum.ALL.equals( + SystemConfiguration.getInstances().getTaskOwnerLockStrategy()) + && BaseConstant.ADMIN_ID != StpUtil.getLoginIdAsInt()) { + Class checkParam = checkTaskOwner.checkParam(); + Object param = getParam(joinPoint, checkParam); + if (Objects.nonNull(param)) { + Object bean = applicationContext.getBean(checkTaskOwner.checkInterface()); + Class clazz = bean.getClass(); + Method method = clazz.getMethod(checkTaskOwner.checkMethod(), param.getClass()); + Object invoke = method.invoke(bean, param); + if (invoke != null && !(Boolean) invoke) { + throw new BusException(Status.TASK_NOT_OPERATE_PERMISSION); + } + } + } + + Object result; + try { + result = joinPoint.proceed(); + } catch (Throwable e) { + throw e; + } + return result; + } + + private Object getParam(ProceedingJoinPoint joinPoint, Class paramAnno) throws IllegalAccessException { + Object[] params = joinPoint.getArgs(); + if (params.length == 0) { + return null; + } + + Object paramObj = null; + // Get the method, here you can convert the signature strong to MethodSignature + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + + Annotation[][] annotations = method.getParameterAnnotations(); + for (int i = 0; i < annotations.length; i++) { + Object param = params[i]; + if (param == null) { + continue; + } + Annotation[] paramAnn = annotations[i]; + for (Annotation annotation : paramAnn) { + if (annotation.annotationType() == paramAnno) { + paramObj = param; + break; + } + } + if (paramObj == null) { + Field[] fields = param.getClass().getDeclaredFields(); + for (Field field : fields) { + if (field.isAnnotationPresent(paramAnno)) { + field.setAccessible(true); + paramObj = field.get(param); + break; + } + } + } + } + return paramObj; + } +} diff --git a/dinky-admin/src/main/java/org/dinky/controller/CatalogueController.java b/dinky-admin/src/main/java/org/dinky/controller/CatalogueController.java index 04d6c3ee5f..18ff3775ae 100644 --- a/dinky-admin/src/main/java/org/dinky/controller/CatalogueController.java +++ b/dinky-admin/src/main/java/org/dinky/controller/CatalogueController.java @@ -19,8 +19,10 @@ package org.dinky.controller; +import org.dinky.data.annotations.CatalogueId; import org.dinky.data.annotations.CheckTaskOwner; import org.dinky.data.annotations.Log; +import org.dinky.data.annotations.TaskId; import org.dinky.data.dto.CatalogueTaskDTO; import org.dinky.data.dto.CatalogueTreeQueryDTO; import org.dinky.data.enums.BusinessType; @@ -165,7 +167,7 @@ public Result> getCatalogueSortType() { required = true, dataType = "CatalogueTaskDTO", dataTypeClass = CatalogueTaskDTO.class) - @CheckTaskOwner(serviceType = TaskService.class) + @CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class) public Result createTask(@RequestBody CatalogueTaskDTO catalogueTaskDTO) { if (catalogueService.checkCatalogueTaskNameIsExistById(catalogueTaskDTO.getName(), catalogueTaskDTO.getId())) { return Result.failed(Status.TASK_IS_EXIST); @@ -201,9 +203,9 @@ public Result createTask(@RequestBody CatalogueTaskDTO catalogueTaskD dataType = "Integer", dataTypeClass = Integer.class) }) - @CheckTaskOwner(serviceType = CatalogueService.class) + @CheckTaskOwner(checkParam = CatalogueId.class, checkInterface = CatalogueService.class) public Result moveCatalogue( - @RequestParam("originCatalogueId") Integer originCatalogueId, + @CatalogueId @RequestParam("originCatalogueId") Integer originCatalogueId, @RequestParam("targetParentId") Integer targetParentId) { if (catalogueService.moveCatalogue(originCatalogueId, targetParentId)) { return Result.succeed(true, Status.MOVE_SUCCESS); @@ -226,7 +228,7 @@ public Result moveCatalogue( dataType = "Catalogue", dataTypeClass = Catalogue.class) @ApiOperation("Copy Task") - @CheckTaskOwner(serviceType = TaskService.class) + @CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class) public Result copyTask(@RequestBody Catalogue catalogue) { if (catalogueService.copyTask(catalogue)) { return Result.succeed(Status.COPY_SUCCESS); @@ -244,8 +246,8 @@ public Result copyTask(@RequestBody Catalogue catalogue) { @Log(title = "Delete Catalogue By Id", businessType = BusinessType.DELETE) @ApiOperation("Delete Catalogue By Id") @ApiImplicitParam(name = "id", value = "id", required = true, dataType = "Integer", dataTypeClass = Integer.class) - @CheckTaskOwner(serviceType = CatalogueService.class) - public Result deleteCatalogueById(@RequestParam Integer id) { + @CheckTaskOwner(checkParam = CatalogueId.class, checkInterface = CatalogueService.class) + public Result deleteCatalogueById(@CatalogueId @RequestParam Integer id) { return catalogueService.deleteCatalogueById(id); } } diff --git a/dinky-admin/src/main/java/org/dinky/controller/TaskController.java b/dinky-admin/src/main/java/org/dinky/controller/TaskController.java index 849b905a75..228020d778 100644 --- a/dinky-admin/src/main/java/org/dinky/controller/TaskController.java +++ b/dinky-admin/src/main/java/org/dinky/controller/TaskController.java @@ -23,6 +23,7 @@ import org.dinky.data.annotations.ExecuteProcess; import org.dinky.data.annotations.Log; import org.dinky.data.annotations.ProcessId; +import org.dinky.data.annotations.TaskId; import org.dinky.data.dto.TaskDTO; import org.dinky.data.dto.TaskRollbackVersionDTO; import org.dinky.data.dto.TaskSaveDTO; @@ -78,8 +79,8 @@ public class TaskController { @ApiOperation("Submit Task") @Log(title = "Submit Task", businessType = BusinessType.SUBMIT) @ExecuteProcess(type = ProcessType.FLINK_SUBMIT) - @CheckTaskOwner(serviceType = TaskService.class) - public Result submitTask(@ProcessId @RequestParam Integer id) throws Exception { + @CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class) + public Result submitTask(@TaskId @ProcessId @RequestParam Integer id) throws Exception { JobResult jobResult = taskService.submitTask(TaskSubmitDto.builder().id(id).build()); if (jobResult.isSuccess()) { @@ -99,7 +100,7 @@ public Result submitTask(@ProcessId @RequestParam Integer id) throws dataType = "DebugDTO", paramType = "body") @ExecuteProcess(type = ProcessType.FLINK_SUBMIT) - @CheckTaskOwner(serviceType = TaskService.class) + @CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class) public Result debugTask(@RequestBody TaskDTO task) throws Exception { JobResult result = taskService.debugTask(task); if (result.isSuccess()) { @@ -111,9 +112,9 @@ public Result debugTask(@RequestBody TaskDTO task) throws Exception { @GetMapping("/cancel") @Log(title = "Cancel Flink Job", businessType = BusinessType.TRIGGER) @ApiOperation("Cancel Flink Job") - @CheckTaskOwner(serviceType = TaskService.class) + @CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class) public Result cancel( - @RequestParam Integer id, + @TaskId @RequestParam Integer id, @RequestParam(defaultValue = "false") boolean withSavePoint, @RequestParam(defaultValue = "false") boolean forceCancel) { if (taskService.cancelTaskJob(taskService.getTaskInfoById(id), withSavePoint, forceCancel)) { @@ -129,8 +130,8 @@ public Result cancel( @GetMapping(value = "/restartTask") @ApiOperation("Restart Task") @Log(title = "Restart Task", businessType = BusinessType.REMOTE_OPERATION) - @CheckTaskOwner(serviceType = TaskService.class) - public Result restartTask(@RequestParam Integer id, String savePointPath) throws Exception { + @CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class) + public Result restartTask(@TaskId @RequestParam Integer id, String savePointPath) throws Exception { JobResult jobResult = taskService.restartTask(id, savePointPath); if (jobResult.isSuccess()) { return Result.succeed(jobResult, Status.RESTART_SUCCESS); @@ -141,8 +142,8 @@ public Result restartTask(@RequestParam Integer id, String savePointP @GetMapping("/savepoint") @Log(title = "Savepoint Trigger", businessType = BusinessType.TRIGGER) @ApiOperation("Savepoint Trigger") - @CheckTaskOwner(serviceType = TaskService.class) - public Result savepoint(@RequestParam Integer taskId, @RequestParam String savePointType) { + @CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class) + public Result savepoint(@TaskId @RequestParam Integer taskId, @RequestParam String savePointType) { return Result.succeed( taskService.savepointTaskJob( taskService.getTaskInfoById(taskId), SavePointType.valueOf(savePointType.toUpperCase())), @@ -152,8 +153,8 @@ public Result savepoint(@RequestParam Integer taskId, @RequestP @GetMapping("/changeTaskLife") @Log(title = "changeTaskLife", businessType = BusinessType.TRIGGER) @ApiOperation("changeTaskLife") - @CheckTaskOwner(serviceType = TaskService.class) - public Result changeTaskLife(@RequestParam Integer taskId, @RequestParam Integer lifeCycle) + @CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class) + public Result changeTaskLife(@TaskId @RequestParam Integer taskId, @RequestParam Integer lifeCycle) throws SqlExplainExcepition { if (taskService.changeTaskLifeRecyle(taskId, JobLifeCycle.get(lifeCycle))) { return Result.succeed(lifeCycle == 2 ? Status.PUBLISH_SUCCESS : Status.OFFLINE_SUCCESS); @@ -164,7 +165,7 @@ public Result changeTaskLife(@RequestParam Integer taskId, @RequestPara @PostMapping("/explainSql") @ApiOperation("Explain Sql") - @CheckTaskOwner(serviceType = TaskService.class) + @CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class) public Result> explainSql(@RequestBody TaskDTO taskDTO) throws NotSupportExplainExcepition { return Result.succeed(taskService.explainTask(taskDTO), Status.EXECUTE_SUCCESS); } @@ -172,7 +173,7 @@ public Result> explainSql(@RequestBody TaskDTO taskDTO) t @PostMapping("/getJobPlan") @ApiOperation("Get Job Plan") @ExecuteProcess(type = ProcessType.FLINK_JOB_PLAN) - @CheckTaskOwner(serviceType = TaskService.class) + @CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class) public Result getJobPlan(@RequestBody TaskDTO taskDTO) { ObjectNode jobPlan = null; jobPlan = taskService.getJobPlan(taskDTO); @@ -189,7 +190,7 @@ public Result getJobPlan(@RequestBody TaskDTO taskDTO) { dataType = "TaskSaveDTO", paramType = "body", dataTypeClass = TaskSaveDTO.class) - @CheckTaskOwner(serviceType = TaskService.class) + @CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class) public Result saveOrUpdateTask(@RequestBody TaskSaveDTO task) { if (taskService.saveOrUpdateTask(task.toTaskEntity())) { return Result.succeed(Status.SAVE_SUCCESS); @@ -233,7 +234,7 @@ public Result> listFlinkSQLEnv() { @PostMapping("/rollbackTask") @ApiOperation("Rollback Task") @Log(title = "Rollback Task", businessType = BusinessType.UPDATE) - @CheckTaskOwner(serviceType = TaskService.class) + @CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class) public Result rollbackTask(@RequestBody TaskRollbackVersionDTO dto) { if (taskService.rollbackTask(dto)) { return Result.succeed(Status.VERSION_ROLLBACK_SUCCESS); diff --git a/dinky-admin/src/main/java/org/dinky/data/dto/CatalogueTaskDTO.java b/dinky-admin/src/main/java/org/dinky/data/dto/CatalogueTaskDTO.java index 1902e4b9cc..0788f60f23 100644 --- a/dinky-admin/src/main/java/org/dinky/data/dto/CatalogueTaskDTO.java +++ b/dinky-admin/src/main/java/org/dinky/data/dto/CatalogueTaskDTO.java @@ -19,6 +19,7 @@ package org.dinky.data.dto; +import org.dinky.data.annotations.TaskId; import org.dinky.data.model.ext.TaskExtConfig; import org.dinky.data.typehandler.ListTypeHandler; @@ -51,6 +52,7 @@ public class CatalogueTaskDTO { private Integer parentId; @ApiModelProperty(value = "Task ID", dataType = "Integer", example = "3", notes = "The ID of the associated task") + @TaskId private Integer taskId; @ApiModelProperty( diff --git a/dinky-admin/src/main/java/org/dinky/data/dto/TaskDTO.java b/dinky-admin/src/main/java/org/dinky/data/dto/TaskDTO.java index 31454442d3..f20f9f2fea 100644 --- a/dinky-admin/src/main/java/org/dinky/data/dto/TaskDTO.java +++ b/dinky-admin/src/main/java/org/dinky/data/dto/TaskDTO.java @@ -20,6 +20,7 @@ package org.dinky.data.dto; import org.dinky.data.annotations.ProcessId; +import org.dinky.data.annotations.TaskId; import org.dinky.data.model.Task; import org.dinky.data.model.alert.AlertGroup; import org.dinky.data.model.ext.TaskExtConfig; @@ -50,6 +51,7 @@ public class TaskDTO extends AbstractStatementDTO { @ApiModelProperty(value = "ID", dataType = "Integer", example = "6", notes = "The identifier of the execution") @ProcessId + @TaskId private Integer id; @ApiModelProperty(value = "Name", required = true, dataType = "String", example = "Name") diff --git a/dinky-admin/src/main/java/org/dinky/data/dto/TaskRollbackVersionDTO.java b/dinky-admin/src/main/java/org/dinky/data/dto/TaskRollbackVersionDTO.java index 3b3cf0e94c..9f2025041c 100644 --- a/dinky-admin/src/main/java/org/dinky/data/dto/TaskRollbackVersionDTO.java +++ b/dinky-admin/src/main/java/org/dinky/data/dto/TaskRollbackVersionDTO.java @@ -19,6 +19,8 @@ package org.dinky.data.dto; +import org.dinky.data.annotations.TaskId; + import java.io.Serializable; import io.swagger.annotations.ApiModel; @@ -31,6 +33,7 @@ public class TaskRollbackVersionDTO implements Serializable { @ApiModelProperty(value = "ID", dataType = "Integer", example = "1", notes = "The identifier of the task") + @TaskId private Integer taskId; @ApiModelProperty( diff --git a/dinky-admin/src/main/java/org/dinky/data/dto/TaskSaveDTO.java b/dinky-admin/src/main/java/org/dinky/data/dto/TaskSaveDTO.java index e91c3b68ec..cb96195e51 100644 --- a/dinky-admin/src/main/java/org/dinky/data/dto/TaskSaveDTO.java +++ b/dinky-admin/src/main/java/org/dinky/data/dto/TaskSaveDTO.java @@ -19,6 +19,7 @@ package org.dinky.data.dto; +import org.dinky.data.annotations.TaskId; import org.dinky.data.model.Task; import org.dinky.data.model.ext.TaskExtConfig; import org.dinky.data.typehandler.JSONObjectHandler; @@ -45,6 +46,7 @@ public class TaskSaveDTO { /** 主键ID */ @TableId(value = "id", type = IdType.AUTO) @ApiModelProperty(value = "ID", required = true, dataType = "Integer", example = "1", notes = "Primary Key") + @TaskId private Integer id; @NotNull( diff --git a/dinky-admin/src/main/java/org/dinky/data/model/Catalogue.java b/dinky-admin/src/main/java/org/dinky/data/model/Catalogue.java index 96a02ecc76..6a401a3877 100644 --- a/dinky-admin/src/main/java/org/dinky/data/model/Catalogue.java +++ b/dinky-admin/src/main/java/org/dinky/data/model/Catalogue.java @@ -19,6 +19,7 @@ package org.dinky.data.model; +import org.dinky.data.annotations.TaskId; import org.dinky.mybatis.model.SuperEntity; import java.util.ArrayList; @@ -47,6 +48,7 @@ public class Catalogue extends SuperEntity { private Integer tenantId; @ApiModelProperty(value = "Task ID", required = true, dataType = "Integer", example = "1") + @TaskId private Integer taskId; @ApiModelProperty(value = "Type", required = true, dataType = "String", example = "Flinksql") diff --git a/dinky-admin/src/main/java/org/dinky/service/TaskService.java b/dinky-admin/src/main/java/org/dinky/service/TaskService.java index 81e52200d8..ab6872e19a 100644 --- a/dinky-admin/src/main/java/org/dinky/service/TaskService.java +++ b/dinky-admin/src/main/java/org/dinky/service/TaskService.java @@ -288,4 +288,12 @@ public interface TaskService extends ISuperService { * @return */ JobConfig buildJobSubmitConfig(TaskDTO task); + + /** + * Check task operate permission. + * Contains reflection invocation. Please do not delete. + * @param taskId + * @return + */ + Boolean checkTaskOperatePermission(Integer taskId); } diff --git a/dinky-admin/src/main/java/org/dinky/service/catalogue/CatalogueService.java b/dinky-admin/src/main/java/org/dinky/service/catalogue/CatalogueService.java index 1805e06875..6d166d0b7a 100644 --- a/dinky-admin/src/main/java/org/dinky/service/catalogue/CatalogueService.java +++ b/dinky-admin/src/main/java/org/dinky/service/catalogue/CatalogueService.java @@ -149,4 +149,12 @@ public interface CatalogueService extends ISuperService { * @return true if the catalogue task name is exist */ boolean checkCatalogueTaskNameIsExistById(String name, Integer id); + + /** + * Check task operate permission. + * Contains reflection invocation. Please do not delete. + * @param catalogueId + * @return + */ + Boolean checkTaskOperatePermission(Integer catalogueId); } diff --git a/dinky-admin/src/main/java/org/dinky/service/catalogue/impl/CatalogueServiceImpl.java b/dinky-admin/src/main/java/org/dinky/service/catalogue/impl/CatalogueServiceImpl.java index d7e5878c99..d990bc04cc 100644 --- a/dinky-admin/src/main/java/org/dinky/service/catalogue/impl/CatalogueServiceImpl.java +++ b/dinky-admin/src/main/java/org/dinky/service/catalogue/impl/CatalogueServiceImpl.java @@ -61,6 +61,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -586,4 +587,13 @@ private CatalogueTaskDTO getCatalogueTaskDTO(String name, Integer parentId) { catalogueTaskDTO.setLeaf(true); return catalogueTaskDTO; } + + @Override + public Boolean checkTaskOperatePermission(Integer catalogueId) { + Catalogue catalogue = getById(catalogueId); + if (Objects.nonNull(catalogue) && catalogue.getIsLeaf() && Objects.nonNull(catalogue.getTaskId())) { + return taskService.checkTaskOperatePermission(catalogue.getTaskId()); + } + return null; + } } diff --git a/dinky-admin/src/main/java/org/dinky/service/impl/TaskServiceImpl.java b/dinky-admin/src/main/java/org/dinky/service/impl/TaskServiceImpl.java index eb1d334ae9..93125f8fd9 100644 --- a/dinky-admin/src/main/java/org/dinky/service/impl/TaskServiceImpl.java +++ b/dinky-admin/src/main/java/org/dinky/service/impl/TaskServiceImpl.java @@ -36,6 +36,7 @@ import org.dinky.data.enums.JobStatus; import org.dinky.data.enums.ProcessStepType; import org.dinky.data.enums.Status; +import org.dinky.data.enums.TaskOwnerLockStrategyEnum; import org.dinky.data.exception.BusException; import org.dinky.data.exception.NotSupportExplainExcepition; import org.dinky.data.exception.SqlExplainExcepition; @@ -129,6 +130,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.tree.Tree; @@ -1008,4 +1010,26 @@ private List> dealWithCatalogue(List catalogueList) } return treeNodes; } + + @Override + public Boolean checkTaskOperatePermission(Integer taskId) { + TaskDTO taskDTO = getTaskInfoById(taskId); + if (Objects.nonNull(taskDTO)) { + return hasTaskOperatePermission(taskDTO.getFirstLevelOwner(), taskDTO.getSecondLevelOwners()); + } + return null; + } + + private Boolean hasTaskOperatePermission(Integer firstLevelOwner, List secondLevelOwners) { + boolean isFirstLevelOwner = firstLevelOwner != null && firstLevelOwner == StpUtil.getLoginIdAsInt(); + if (TaskOwnerLockStrategyEnum.OWNER.equals( + SystemConfiguration.getInstances().getTaskOwnerLockStrategy())) { + return isFirstLevelOwner; + } else if (TaskOwnerLockStrategyEnum.OWNER_AND_MAINTAINER.equals( + SystemConfiguration.getInstances().getTaskOwnerLockStrategy())) { + return isFirstLevelOwner + || (secondLevelOwners != null && secondLevelOwners.contains(StpUtil.getLoginIdAsInt())); + } + return true; + } } diff --git a/dinky-common/src/main/java/org/dinky/data/annotations/CatalogueId.java b/dinky-common/src/main/java/org/dinky/data/annotations/CatalogueId.java new file mode 100644 index 0000000000..570a007d5b --- /dev/null +++ b/dinky-common/src/main/java/org/dinky/data/annotations/CatalogueId.java @@ -0,0 +1,31 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.dinky.data.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.PARAMETER, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface CatalogueId {} diff --git a/dinky-common/src/main/java/org/dinky/data/annotations/CheckTaskOwner.java b/dinky-common/src/main/java/org/dinky/data/annotations/CheckTaskOwner.java index 6510d60ca0..aa532ad68f 100644 --- a/dinky-common/src/main/java/org/dinky/data/annotations/CheckTaskOwner.java +++ b/dinky-common/src/main/java/org/dinky/data/annotations/CheckTaskOwner.java @@ -29,5 +29,9 @@ @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface CheckTaskOwner { - Class serviceType(); + Class checkParam(); + + String checkMethod() default "checkTaskOperatePermission"; + + Class checkInterface(); } diff --git a/dinky-common/src/main/java/org/dinky/data/annotations/TaskId.java b/dinky-common/src/main/java/org/dinky/data/annotations/TaskId.java new file mode 100644 index 0000000000..052d056a1a --- /dev/null +++ b/dinky-common/src/main/java/org/dinky/data/annotations/TaskId.java @@ -0,0 +1,31 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.dinky.data.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.PARAMETER, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface TaskId {}