From 165d7aa51f1be5aba86752b27067ec9888582acf Mon Sep 17 00:00:00 2001 From: qianli2022 <97489722+qianli2022@users.noreply.github.com> Date: Wed, 20 Apr 2022 18:23:23 +0800 Subject: [PATCH] [Feature][Task] K8s namespace auth manager (#9303) * k8s auth * remove log * fix test * use constants * use constants K8S_LOCAL_TEST_CLUSTER * simple auth get * change test * add namespace authorize in user page * prettier code * change test data Co-authored-by: qianl4 Co-authored-by: William Tong --- .../controller/K8sNamespaceController.java | 91 ++++++++-- .../api/controller/UsersController.java | 26 +++ .../dolphinscheduler/api/enums/Status.java | 6 +- ...eService.java => K8sNamespaceService.java} | 39 ++++- .../api/service/UsersService.java | 11 ++ ...Impl.java => K8SNamespaceServiceImpl.java} | 155 +++++++++++++---- .../api/service/impl/UsersServiceImpl.java | 53 ++++++ .../K8sNamespaceControllerTest.java | 45 ++++- ...Test.java => K8SNamespaceServiceTest.java} | 80 +++++++-- .../api/service/UsersServiceTest.java | 21 +++ .../dolphinscheduler/common/Constants.java | 1 + .../dao/entity/K8sNamespace.java | 92 ++++++++-- .../dao/entity/K8sNamespaceUser.java | 164 ++++++++++++++++++ .../dao/mapper/K8sNamespaceMapper.java | 26 +++ .../dao/mapper/K8sNamespaceUserMapper.java | 50 ++++++ .../dao/mapper/K8sNamespaceMapper.xml | 33 +++- .../dao/mapper/K8sNamespaceUserMapper.xml | 40 +++++ .../resources/sql/dolphinscheduler_h2.sql | 31 +++- .../resources/sql/dolphinscheduler_mysql.sql | 17 +- .../sql/dolphinscheduler_postgresql.sql | 19 +- .../mysql/dolphinscheduler_ddl.sql | 19 +- .../postgresql/dolphinscheduler_ddl.sql | 16 +- .../src/locales/modules/en_US.ts | 2 + .../src/locales/modules/zh_CN.ts | 2 + .../service/modules/k8s-namespace/index.ts | 17 ++ .../src/service/modules/users/index.ts | 9 + .../src/service/modules/users/types.ts | 5 + .../components/authorize-modal.tsx | 9 + .../user-manage/components/use-authorize.ts | 37 +++- .../src/views/security/user-manage/types.ts | 1 + .../views/security/user-manage/use-columns.ts | 6 +- .../namespace/_source/createNamespace.vue | 23 +-- .../security/pages/namespace/_source/list.vue | 2 - .../security/pages/users/_source/list.vue | 52 +++++- .../module/components/transfer/transfer.vue | 2 + .../src/js/module/i18n/locale/en_US.js | 7 +- .../src/js/module/i18n/locale/zh_CN.js | 3 +- 37 files changed, 1074 insertions(+), 138 deletions(-) rename dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/{K8sNameSpaceService.java => K8sNamespaceService.java} (71%) rename dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/{K8sNameSpaceServiceImpl.java => K8SNamespaceServiceImpl.java} (66%) rename dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/{K8sNameSpaceServiceTest.java => K8SNamespaceServiceTest.java} (64%) create mode 100644 dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/K8sNamespaceUser.java create mode 100644 dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceUserMapper.java create mode 100644 dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceUserMapper.xml diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/K8sNamespaceController.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/K8sNamespaceController.java index ddae560d67..ac2e29534d 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/K8sNamespaceController.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/K8sNamespaceController.java @@ -19,18 +19,23 @@ package org.apache.dolphinscheduler.api.controller; import static org.apache.dolphinscheduler.api.enums.Status.CREATE_K8S_NAMESPACE_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.DELETE_K8S_NAMESPACE_BY_ID_ERROR; +import static org.apache.dolphinscheduler.api.enums.Status.QUERY_AUTHORIZED_NAMESPACE_ERROR; +import static org.apache.dolphinscheduler.api.enums.Status.QUERY_CAN_USE_K8S_CLUSTER_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.QUERY_K8S_NAMESPACE_LIST_PAGING_ERROR; +import static org.apache.dolphinscheduler.api.enums.Status.QUERY_UNAUTHORIZED_NAMESPACE_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.UPDATE_K8S_NAMESPACE_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.VERIFY_K8S_NAMESPACE_ERROR; import org.apache.dolphinscheduler.api.aspect.AccessLogAnnotation; import org.apache.dolphinscheduler.api.exceptions.ApiException; -import org.apache.dolphinscheduler.api.service.K8sNameSpaceService; +import org.apache.dolphinscheduler.api.service.K8sNamespaceService; import org.apache.dolphinscheduler.api.utils.Result; import org.apache.dolphinscheduler.common.Constants; import org.apache.dolphinscheduler.common.utils.ParameterUtils; +import org.apache.dolphinscheduler.dao.entity.K8sNamespace; import org.apache.dolphinscheduler.dao.entity.User; +import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; @@ -60,7 +65,7 @@ import springfox.documentation.annotations.ApiIgnore; public class K8sNamespaceController extends BaseController { @Autowired - private K8sNameSpaceService k8sNameSpaceService; + private K8sNamespaceService k8sNamespaceService; /** * query namespace list paging @@ -92,7 +97,7 @@ public class K8sNamespaceController extends BaseController { return result; } searchVal = ParameterUtils.handleEscapes(searchVal); - result = k8sNameSpaceService.queryListPaging(loginUser, searchVal, pageNo, pageSize); + result = k8sNamespaceService.queryListPaging(loginUser, searchVal, pageNo, pageSize); return result; } @@ -102,8 +107,6 @@ public class K8sNamespaceController extends BaseController { * * @param loginUser * @param namespace k8s namespace - * @param owner owner - * @param tag Which type of job is available, can be empty, all available * @param k8s k8s name * @param limitsCpu max cpu * @param limitsMemory max memory @@ -112,8 +115,6 @@ public class K8sNamespaceController extends BaseController { @ApiOperation(value = "createK8sNamespace", notes = "CREATE_NAMESPACE_NOTES") @ApiImplicitParams({ @ApiImplicitParam(name = "namespace", value = "NAMESPACE", required = true, dataType = "String"), - @ApiImplicitParam(name = "owner", value = "OWNER", required = false, dataType = "String"), - @ApiImplicitParam(name = "tag", value = "TAG", required = false, dataType = "String"), @ApiImplicitParam(name = "k8s", value = "K8S", required = true, dataType = "String"), @ApiImplicitParam(name = "limits_cpu", value = "LIMITS_CPU", required = false, dataType = "Double"), @ApiImplicitParam(name = "limits_memory", value = "LIMITS_MEMORY", required = false, dataType = "Integer") @@ -125,12 +126,10 @@ public class K8sNamespaceController extends BaseController { public Result createNamespace(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser, @RequestParam(value = "namespace") String namespace, @RequestParam(value = "k8s") String k8s, - @RequestParam(value = "owner", required = false) String owner, - @RequestParam(value = "tag", required = false) String tag, @RequestParam(value = "limitsCpu", required = false) Double limitsCpu, @RequestParam(value = "limitsMemory", required = false) Integer limitsMemory ) { - Map result = k8sNameSpaceService.createK8sNamespace(loginUser, namespace, k8s, owner, tag, limitsCpu, limitsMemory); + Map result = k8sNamespaceService.createK8sNamespace(loginUser, namespace, k8s, limitsCpu, limitsMemory); return returnDataList(result); } @@ -138,8 +137,7 @@ public class K8sNamespaceController extends BaseController { * update namespace,namespace and k8s not allowed update, because may create on k8s,can delete and create new instead * * @param loginUser - * @param owner owner - * @param tag Which type of job is available,such as flink,means only flink job can use, can be empty, all available + * @param userName owner * @param limitsCpu max cpu * @param limitsMemory max memory * @return @@ -147,8 +145,7 @@ public class K8sNamespaceController extends BaseController { @ApiOperation(value = "updateK8sNamespace", notes = "UPDATE_NAMESPACE_NOTES") @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "K8S_NAMESPACE_ID", required = true, dataType = "Int", example = "100"), - @ApiImplicitParam(name = "owner", value = "OWNER", required = false, dataType = "String"), - @ApiImplicitParam(name = "tag", value = "TAG", required = false, dataType = "String"), + @ApiImplicitParam(name = "userName", value = "OWNER", required = false, dataType = "String"), @ApiImplicitParam(name = "limitsCpu", value = "LIMITS_CPU", required = false, dataType = "Double"), @ApiImplicitParam(name = "limitsMemory", value = "LIMITS_MEMORY", required = false, dataType = "Integer")}) @PutMapping(value = "/{id}") @@ -157,11 +154,11 @@ public class K8sNamespaceController extends BaseController { @AccessLogAnnotation(ignoreRequestArgs = "loginUser") public Result updateNamespace(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser, @PathVariable(value = "id") int id, - @RequestParam(value = "owner", required = false) String owner, + @RequestParam(value = "userName", required = false) String userName, @RequestParam(value = "tag", required = false) String tag, @RequestParam(value = "limitsCpu", required = false) Double limitsCpu, @RequestParam(value = "limitsMemory", required = false) Integer limitsMemory) { - Map result = k8sNameSpaceService.updateK8sNamespace(loginUser, id, owner, tag, limitsCpu, limitsMemory); + Map result = k8sNamespaceService.updateK8sNamespace(loginUser, id, userName, limitsCpu, limitsMemory); return returnDataList(result); } @@ -187,7 +184,7 @@ public class K8sNamespaceController extends BaseController { @RequestParam(value = "k8s") String k8s ) { - return k8sNameSpaceService.verifyNamespaceK8s(namespace, k8s); + return k8sNamespaceService.verifyNamespaceK8s(namespace, k8s); } @@ -208,7 +205,65 @@ public class K8sNamespaceController extends BaseController { @AccessLogAnnotation public Result delNamespaceById(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser, @RequestParam(value = "id") int id) { - Map result = k8sNameSpaceService.deleteNamespaceById(loginUser, id); + Map result = k8sNamespaceService.deleteNamespaceById(loginUser, id); + return returnDataList(result); + } + + /** + * query unauthorized namespace + * + * @param loginUser login user + * @param userId user id + * @return the namespaces which user have not permission to see + */ + @ApiOperation(value = "queryUnauthorizedNamespace", notes = "QUERY_UNAUTHORIZED_NAMESPACE_NOTES") + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", value = "USER_ID", dataType = "Int", example = "100") + }) + @GetMapping(value = "/unauth-namespace") + @ResponseStatus(HttpStatus.OK) + @ApiException(QUERY_UNAUTHORIZED_NAMESPACE_ERROR) + @AccessLogAnnotation(ignoreRequestArgs = "loginUser") + public Result queryUnauthorizedNamespace(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + @RequestParam("userId") Integer userId) { + Map result = k8sNamespaceService.queryUnauthorizedNamespace(loginUser, userId); return returnDataList(result); } + + /** + * query unauthorized namespace + * + * @param loginUser login user + * @param userId user id + * @return namespaces which the user have permission to see + */ + @ApiOperation(value = "queryAuthorizedNamespace", notes = "QUERY_AUTHORIZED_NAMESPACE_NOTES") + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", value = "USER_ID", dataType = "Int", example = "100") + }) + @GetMapping(value = "/authed-namespace") + @ResponseStatus(HttpStatus.OK) + @ApiException(QUERY_AUTHORIZED_NAMESPACE_ERROR) + @AccessLogAnnotation(ignoreRequestArgs = "loginUser") + public Result queryAuthorizedNamespace(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + @RequestParam("userId") Integer userId) { + Map result = k8sNamespaceService.queryAuthorizedNamespace(loginUser, userId); + return returnDataList(result); + } + + /** + * query namespace available + * + * @param loginUser login user + * @return namespace list + */ + @ApiOperation(value = "queryAvailableNamespaceList", notes = "QUERY_AVAILABLE_NAMESPACE_LIST_NOTES") + @GetMapping(value = "/available-list") + @ResponseStatus(HttpStatus.OK) + @ApiException(QUERY_CAN_USE_K8S_CLUSTER_ERROR) + @AccessLogAnnotation(ignoreRequestArgs = "loginUser") + public Result queryAvailableNamespaceList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser) { + List result = k8sNamespaceService.queryNamespaceAvailable(loginUser); + return success(result); + } } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/UsersController.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/UsersController.java index 313958b22b..81f0041db0 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/UsersController.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/UsersController.java @@ -22,6 +22,7 @@ import static org.apache.dolphinscheduler.api.enums.Status.CREATE_USER_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.DELETE_USER_BY_ID_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.GET_USER_INFO_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.GRANT_DATASOURCE_ERROR; +import static org.apache.dolphinscheduler.api.enums.Status.GRANT_K8S_NAMESPACE_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.GRANT_PROJECT_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.GRANT_RESOURCE_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.GRANT_UDF_FUNCTION_ERROR; @@ -334,6 +335,31 @@ public class UsersController extends BaseController { } + /** + * grant namespace + * + * @param loginUser login user + * @param userId user id + * @param namespaceIds namespace id array + * @return grant result code + */ + @ApiOperation(value = "grantNamespace", notes = "GRANT_NAMESPACE_NOTES") + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", value = "USER_ID", required = true, dataType = "Int", example = "100"), + @ApiImplicitParam(name = "namespaceIds", value = "NAMESPACE_IDS", required = true, type = "String") + }) + @PostMapping(value = "/grant-namespace") + @ResponseStatus(HttpStatus.OK) + @ApiException(GRANT_K8S_NAMESPACE_ERROR) + @AccessLogAnnotation + public Result grantNamespace(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + @RequestParam(value = "userId") int userId, + @RequestParam(value = "namespaceIds") String namespaceIds) { + Map result = usersService.grantNamespaces(loginUser, userId, namespaceIds); + return returnDataList(result); + } + + /** * grant datasource * diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java index 4954adf431..ff844ad96d 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java @@ -394,7 +394,11 @@ public enum Status { VERIFY_K8S_NAMESPACE_ERROR(1300007, "verify k8s and namespace error", "验证k8s命名空间信息错误"), DELETE_K8S_NAMESPACE_BY_ID_ERROR(1300008, "delete k8s namespace by id error", "删除命名空间错误"), VERIFY_PARAMETER_NAME_FAILED(1300009, "The file name verify failed", "文件命名校验失败"), - STORE_OPERATE_CREATE_ERROR(1300010, "create the resource failed", "存储操作失败"); + STORE_OPERATE_CREATE_ERROR(1300010, "create the resource failed", "存储操作失败"), + GRANT_K8S_NAMESPACE_ERROR(1300011, "grant namespace error", "授权资源错误"), + QUERY_UNAUTHORIZED_NAMESPACE_ERROR(1300012, "query unauthorized namespace error", "查询未授权命名空间错误"), + QUERY_AUTHORIZED_NAMESPACE_ERROR(1300013, "query authorized namespace error", "查询授权命名空间错误"), + QUERY_CAN_USE_K8S_CLUSTER_ERROR(1300014, "login user query can used k8s cluster list error", "查询可用k8s集群错误"); private final int code; private final String enMsg; diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/K8sNameSpaceService.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/K8sNamespaceService.java similarity index 71% rename from dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/K8sNameSpaceService.java rename to dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/K8sNamespaceService.java index c9156a38f3..d56c8ab537 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/K8sNameSpaceService.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/K8sNamespaceService.java @@ -18,14 +18,16 @@ package org.apache.dolphinscheduler.api.service; import org.apache.dolphinscheduler.api.utils.Result; +import org.apache.dolphinscheduler.dao.entity.K8sNamespace; import org.apache.dolphinscheduler.dao.entity.User; +import java.util.List; import java.util.Map; /** * k8s namespace service impl */ -public interface K8sNameSpaceService { +public interface K8sNamespaceService { /** * query namespace list paging * @@ -44,26 +46,23 @@ public interface K8sNameSpaceService { * @param loginUser login user * @param namespace namespace * @param k8s k8s not null - * @param owner owner can null - * @param tag can null,if set means just used for one type job,such as flink,spark * @param limitsCpu limits cpu, can null means not limit * @param limitsMemory limits memory, can null means not limit * @return */ - Map createK8sNamespace(User loginUser, String namespace, String k8s, String owner, String tag, Double limitsCpu, Integer limitsMemory); + Map createK8sNamespace(User loginUser, String namespace, String k8s, Double limitsCpu, Integer limitsMemory); /** * update K8s Namespace tag and resource limit * * @param loginUser login user - * @param owner owner - * @param tag Which type of job is available,such as flink,means only flink job can use, can be empty, all available + * @param userName owner * @param limitsCpu max cpu * @param limitsMemory max memory * @return */ - Map updateK8sNamespace(User loginUser, int id, String owner, String tag, Double limitsCpu, Integer limitsMemory); + Map updateK8sNamespace(User loginUser, int id, String userName, Double limitsCpu, Integer limitsMemory); /** * verify namespace and k8s @@ -82,4 +81,30 @@ public interface K8sNameSpaceService { * @return */ Map deleteNamespaceById(User loginUser, int id); + + /** + * query unauthorized namespace + * + * @param loginUser login user + * @param userId user id + * @return the namespaces which user have not permission to see + */ + Map queryUnauthorizedNamespace(User loginUser, Integer userId); + + /** + * query unauthorized namespace + * + * @param loginUser login user + * @param userId user id + * @return namespaces which the user have permission to see + */ + Map queryAuthorizedNamespace(User loginUser, Integer userId); + + /** + * query namespace can use + * + * @param loginUser login user + * @return namespace list + */ + List queryNamespaceAvailable(User loginUser); } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/UsersService.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/UsersService.java index 3ae4c689b1..4a2afe547f 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/UsersService.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/UsersService.java @@ -194,6 +194,17 @@ public interface UsersService { Map grantUDFFunction(User loginUser, int userId, String udfIds); + /** + * grant namespace + * + * @param loginUser login user + * @param userId user id + * @param namespaceIds namespace id array + * @return grant result code + */ + Map grantNamespaces(User loginUser, int userId, String namespaceIds); + + /** * grant datasource * diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/K8sNameSpaceServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/K8SNamespaceServiceImpl.java similarity index 66% rename from dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/K8sNameSpaceServiceImpl.java rename to dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/K8SNamespaceServiceImpl.java index faa8a24cb3..f6e393f6ab 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/K8sNameSpaceServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/K8SNamespaceServiceImpl.java @@ -18,7 +18,7 @@ package org.apache.dolphinscheduler.api.service.impl; import org.apache.dolphinscheduler.api.enums.Status; -import org.apache.dolphinscheduler.api.service.K8sNameSpaceService; +import org.apache.dolphinscheduler.api.service.K8sNamespaceService; import org.apache.dolphinscheduler.api.utils.PageInfo; import org.apache.dolphinscheduler.api.utils.Result; import org.apache.dolphinscheduler.common.Constants; @@ -29,15 +29,20 @@ import org.apache.dolphinscheduler.service.k8s.K8sClientService; import org.apache.commons.lang.StringUtils; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -45,9 +50,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; * k8s namespace service impl */ @Service -public class K8sNameSpaceServiceImpl extends BaseServiceImpl implements K8sNameSpaceService { +public class K8SNamespaceServiceImpl extends BaseServiceImpl implements K8sNamespaceService { - private static final Logger logger = LoggerFactory.getLogger(K8sNameSpaceServiceImpl.class); + private static final Logger logger = LoggerFactory.getLogger(K8SNamespaceServiceImpl.class); private static String resourceYaml = "apiVersion: v1\n" + "kind: ResourceQuota\n" @@ -100,14 +105,12 @@ public class K8sNameSpaceServiceImpl extends BaseServiceImpl implements K8sNameS * @param loginUser login user * @param namespace namespace * @param k8s k8s not null - * @param owner owner can null - * @param tag can null,if set means just used for one type job,such as flink,spark * @param limitsCpu limits cpu, can null means not limit * @param limitsMemory limits memory, can null means not limit * @return */ @Override - public Map createK8sNamespace(User loginUser, String namespace, String k8s, String owner, String tag, Double limitsCpu, Integer limitsMemory) { + public Map createK8sNamespace(User loginUser, String namespace, String k8s, Double limitsCpu, Integer limitsMemory) { Map result = new HashMap<>(); if (isNotAdmin(loginUser, result)) { return result; @@ -143,8 +146,7 @@ public class K8sNameSpaceServiceImpl extends BaseServiceImpl implements K8sNameS k8sNamespaceObj.setNamespace(namespace); k8sNamespaceObj.setK8s(k8s); - k8sNamespaceObj.setOwner(owner); - k8sNamespaceObj.setTag(tag); + k8sNamespaceObj.setUserId(loginUser.getId()); k8sNamespaceObj.setLimitsCpu(limitsCpu); k8sNamespaceObj.setLimitsMemory(limitsMemory); k8sNamespaceObj.setOnlineJobNum(0); @@ -154,13 +156,15 @@ public class K8sNameSpaceServiceImpl extends BaseServiceImpl implements K8sNameS k8sNamespaceObj.setCreateTime(now); k8sNamespaceObj.setUpdateTime(now); - try { - String yamlStr = genDefaultResourceYaml(k8sNamespaceObj); - k8sClientService.upsertNamespaceAndResourceToK8s(k8sNamespaceObj, yamlStr); - } catch (Exception e) { - logger.error("namespace create to k8s error", e); - putMsg(result, Status.K8S_CLIENT_OPS_ERROR, e.getMessage()); - return result; + if (!Constants.K8S_LOCAL_TEST_CLUSTER.equals(k8sNamespaceObj.getK8s())) { + try { + String yamlStr = genDefaultResourceYaml(k8sNamespaceObj); + k8sClientService.upsertNamespaceAndResourceToK8s(k8sNamespaceObj, yamlStr); + } catch (Exception e) { + logger.error("namespace create to k8s error", e); + putMsg(result, Status.K8S_CLIENT_OPS_ERROR, e.getMessage()); + return result; + } } k8sNamespaceMapper.insert(k8sNamespaceObj); @@ -173,14 +177,13 @@ public class K8sNameSpaceServiceImpl extends BaseServiceImpl implements K8sNameS * update K8s Namespace tag and resource limit * * @param loginUser login user - * @param owner owner - * @param tag Which type of job is available,such as flink,means only flink job can use, can be empty, all available + * @param userName owner * @param limitsCpu max cpu * @param limitsMemory max memory * @return */ @Override - public Map updateK8sNamespace(User loginUser, int id, String owner, String tag, Double limitsCpu, Integer limitsMemory) { + public Map updateK8sNamespace(User loginUser, int id, String userName, Double limitsCpu, Integer limitsMemory) { Map result = new HashMap<>(); if (isNotAdmin(loginUser, result)) { return result; @@ -203,18 +206,19 @@ public class K8sNameSpaceServiceImpl extends BaseServiceImpl implements K8sNameS } Date now = new Date(); - k8sNamespaceObj.setTag(tag); k8sNamespaceObj.setLimitsCpu(limitsCpu); k8sNamespaceObj.setLimitsMemory(limitsMemory); k8sNamespaceObj.setUpdateTime(now); - k8sNamespaceObj.setOwner(owner); - try { - String yamlStr = genDefaultResourceYaml(k8sNamespaceObj); - k8sClientService.upsertNamespaceAndResourceToK8s(k8sNamespaceObj, yamlStr); - } catch (Exception e) { - logger.error("namespace update to k8s error", e); - putMsg(result, Status.K8S_CLIENT_OPS_ERROR, e.getMessage()); - return result; + + if (!Constants.K8S_LOCAL_TEST_CLUSTER.equals(k8sNamespaceObj.getK8s())) { + try { + String yamlStr = genDefaultResourceYaml(k8sNamespaceObj); + k8sClientService.upsertNamespaceAndResourceToK8s(k8sNamespaceObj, yamlStr); + } catch (Exception e) { + logger.error("namespace update to k8s error", e); + putMsg(result, Status.K8S_CLIENT_OPS_ERROR, e.getMessage()); + return result; + } } // update to db k8sNamespaceMapper.updateById(k8sNamespaceObj); @@ -271,8 +275,9 @@ public class K8sNameSpaceServiceImpl extends BaseServiceImpl implements K8sNameS putMsg(result, Status.K8S_NAMESPACE_NOT_EXIST, id); return result; } - - k8sClientService.deleteNamespaceToK8s(k8sNamespaceObj.getNamespace(), k8sNamespaceObj.getK8s()); + if (!Constants.K8S_LOCAL_TEST_CLUSTER.equals(k8sNamespaceObj.getK8s())) { + k8sClientService.deleteNamespaceToK8s(k8sNamespaceObj.getNamespace(), k8sNamespaceObj.getK8s()); + } k8sNamespaceMapper.deleteById(id); putMsg(result, Status.SUCCESS); return result; @@ -323,4 +328,96 @@ public class K8sNameSpaceServiceImpl extends BaseServiceImpl implements K8sNameS } return result; } + + + /** + * query unauthorized namespace + * + * @param loginUser login user + * @param userId user id + * @return the namespaces which user have not permission to see + */ + @Override + public Map queryUnauthorizedNamespace(User loginUser, Integer userId) { + Map result = new HashMap<>(); + if (loginUser.getId() != userId && isNotAdmin(loginUser, result)) { + return result; + } + // query all namespace list,this auth does not like project + List namespaceList = k8sNamespaceMapper.selectList(null); + List resultList = new ArrayList<>(); + Set namespaceSet; + if (namespaceList != null && !namespaceList.isEmpty()) { + namespaceSet = new HashSet<>(namespaceList); + List authedProjectList = k8sNamespaceMapper.queryAuthedNamespaceListByUserId(userId); + resultList = getUnauthorizedNamespaces(namespaceSet, authedProjectList); + } + result.put(Constants.DATA_LIST, resultList); + putMsg(result, Status.SUCCESS); + return result; + } + + /** + * query authorized namespace + * + * @param loginUser login user + * @param userId user id + * @return namespaces which the user have permission to see + */ + @Override + public Map queryAuthorizedNamespace(User loginUser, Integer userId) { + Map result = new HashMap<>(); + + if (loginUser.getId() != userId && isNotAdmin(loginUser, result)) { + return result; + } + + List namespaces = k8sNamespaceMapper.queryAuthedNamespaceListByUserId(userId); + result.put(Constants.DATA_LIST, namespaces); + putMsg(result, Status.SUCCESS); + + return result; + } + + /** + * query namespace can use + * + * @param loginUser login user + * @return namespace list + */ + @Override + public List queryNamespaceAvailable(User loginUser) { + if (isAdmin(loginUser)) { + return k8sNamespaceMapper.selectList(null); + } else { + return k8sNamespaceMapper.queryNamespaceAvailable(loginUser.getId()); + } + } + + /** + * get unauthorized namespace + * + * @param namespaceSet namespace set + * @param authedNamespaceList authed namespace list + * @return namespace list that authorization + */ + private List getUnauthorizedNamespaces(Set namespaceSet, List authedNamespaceList) { + List resultList = new ArrayList<>(); + for (K8sNamespace k8sNamespace : namespaceSet) { + boolean existAuth = false; + if (authedNamespaceList != null && !authedNamespaceList.isEmpty()) { + for (K8sNamespace k8sNamespaceAuth : authedNamespaceList) { + if (k8sNamespace.equals(k8sNamespaceAuth)) { + existAuth = true; + } + } + } + + if (!existAuth) { + resultList.add(k8sNamespace); + } + } + return resultList; + } + } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java index f83ffbfba7..ac6b38adcc 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java @@ -36,6 +36,7 @@ import org.apache.dolphinscheduler.common.utils.EncryptionUtils; import org.apache.dolphinscheduler.common.utils.PropertyUtils; import org.apache.dolphinscheduler.dao.entity.AlertGroup; import org.apache.dolphinscheduler.dao.entity.DatasourceUser; +import org.apache.dolphinscheduler.dao.entity.K8sNamespaceUser; import org.apache.dolphinscheduler.dao.entity.Project; import org.apache.dolphinscheduler.dao.entity.ProjectUser; import org.apache.dolphinscheduler.dao.entity.Resource; @@ -46,6 +47,7 @@ import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.mapper.AccessTokenMapper; import org.apache.dolphinscheduler.dao.mapper.AlertGroupMapper; import org.apache.dolphinscheduler.dao.mapper.DataSourceUserMapper; +import org.apache.dolphinscheduler.dao.mapper.K8sNamespaceUserMapper; import org.apache.dolphinscheduler.dao.mapper.ProcessDefinitionMapper; import org.apache.dolphinscheduler.dao.mapper.ProjectMapper; import org.apache.dolphinscheduler.dao.mapper.ProjectUserMapper; @@ -117,6 +119,9 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService { @Autowired(required = false) private StorageOperate storageOperate; + @Autowired + private K8sNamespaceUserMapper k8sNamespaceUserMapper; + /** * create user, only system admin have permission * @@ -798,6 +803,54 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService { return result; } + + /** + * grant namespace + * + * @param loginUser login user + * @param userId user id + * @param namespaceIds namespace id array + * @return grant result code + */ + @Override + @Transactional(rollbackFor = RuntimeException.class) + public Map grantNamespaces(User loginUser, int userId, String namespaceIds) { + Map result = new HashMap<>(); + result.put(Constants.STATUS, false); + + //only admin can operate + if (check(result, !isAdmin(loginUser), Status.USER_NO_OPERATION_PERM)) { + return result; + } + + //check exist + User tempUser = userMapper.selectById(userId); + if (tempUser == null) { + putMsg(result, Status.USER_NOT_EXIST, userId); + return result; + } + + k8sNamespaceUserMapper.deleteNamespaceRelation(0, userId); + if (StringUtils.isNotEmpty(namespaceIds)) { + String[] namespaceIdArr = namespaceIds.split(","); + for (String namespaceId : namespaceIdArr) { + Date now = new Date(); + K8sNamespaceUser namespaceUser = new K8sNamespaceUser(); + namespaceUser.setUserId(userId); + namespaceUser.setNamespaceId(Integer.parseInt(namespaceId)); + namespaceUser.setPerm(7); + namespaceUser.setCreateTime(now); + namespaceUser.setUpdateTime(now); + k8sNamespaceUserMapper.insert(namespaceUser); + } + } + + putMsg(result, Status.SUCCESS); + + return result; + } + + /** * grant datasource * diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/K8sNamespaceControllerTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/K8sNamespaceControllerTest.java index 830943ba0d..d265ebb118 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/K8sNamespaceControllerTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/K8sNamespaceControllerTest.java @@ -28,8 +28,12 @@ import org.apache.dolphinscheduler.api.utils.Result; import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.entity.User; +import java.util.HashMap; +import java.util.Map; + import org.junit.Assert; import org.junit.Test; +import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; @@ -96,7 +100,7 @@ public class K8sNamespaceControllerTest extends AbstractControllerTest { .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andReturn(); Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); - Assert.assertEquals(Status.K8S_CLIENT_OPS_ERROR.getCode(), result.getCode().intValue()); + Assert.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); logger.info("update queue return result:{}", mvcResult.getResponse().getContentAsString()); } @@ -137,7 +141,7 @@ public class K8sNamespaceControllerTest extends AbstractControllerTest { } @Test - public void delNamespaceById() throws Exception { + public void deleteNamespaceById() throws Exception { MultiValueMap paramsMap = new LinkedMultiValueMap<>(); paramsMap.add("id", "1"); @@ -149,7 +153,42 @@ public class K8sNamespaceControllerTest extends AbstractControllerTest { .andReturn(); Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); - Assert.assertEquals(Status.DELETE_K8S_NAMESPACE_BY_ID_ERROR.getCode(), result.getCode().intValue());//there is no k8s cluster in test env + Assert.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue());//there is no k8s cluster in test env + logger.info(mvcResult.getResponse().getContentAsString()); + } + + @Test + public void testQueryUnauthorizedNamespace() throws Exception { + + MultiValueMap paramsMap = new LinkedMultiValueMap<>(); + paramsMap.add("userId", "1"); + + MvcResult mvcResult = mockMvc.perform(get("/k8s-namespace/unauth-namespace") + .header(SESSION_ID, sessionId) + .params(paramsMap)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andReturn(); + + Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); + Assert.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); + logger.info(mvcResult.getResponse().getContentAsString()); + } + + @Test + public void testQueryAuthorizedNamespace() throws Exception { + MultiValueMap paramsMap = new LinkedMultiValueMap<>(); + paramsMap.add("userId", "1"); + + MvcResult mvcResult = mockMvc.perform(get("/k8s-namespace/authed-namespace") + .header(SESSION_ID, sessionId) + .params(paramsMap)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andReturn(); + + Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); + Assert.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); logger.info(mvcResult.getResponse().getContentAsString()); } } \ No newline at end of file diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/K8sNameSpaceServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/K8SNamespaceServiceTest.java similarity index 64% rename from dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/K8sNameSpaceServiceTest.java rename to dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/K8SNamespaceServiceTest.java index 26735565d1..3ab626c5a0 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/K8sNameSpaceServiceTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/K8SNamespaceServiceTest.java @@ -18,7 +18,7 @@ package org.apache.dolphinscheduler.api.service; import org.apache.dolphinscheduler.api.enums.Status; -import org.apache.dolphinscheduler.api.service.impl.K8sNameSpaceServiceImpl; +import org.apache.dolphinscheduler.api.service.impl.K8SNamespaceServiceImpl; import org.apache.dolphinscheduler.api.utils.PageInfo; import org.apache.dolphinscheduler.api.utils.Result; import org.apache.dolphinscheduler.common.Constants; @@ -51,12 +51,12 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @RunWith(MockitoJUnitRunner.class) -public class K8sNameSpaceServiceTest { +public class K8SNamespaceServiceTest { - private static final Logger logger = LoggerFactory.getLogger(K8sNameSpaceServiceTest.class); + private static final Logger logger = LoggerFactory.getLogger(K8SNamespaceServiceTest.class); @InjectMocks - private K8sNameSpaceServiceImpl k8sNameSpaceService; + private K8SNamespaceServiceImpl k8sNamespaceService; @Mock private K8sNamespaceMapper k8sNamespaceMapper; @@ -86,7 +86,7 @@ public class K8sNameSpaceServiceTest { page.setTotal(1L); page.setRecords(getNamespaceList()); Mockito.when(k8sNamespaceMapper.queryK8sNamespacePaging(Mockito.any(Page.class), Mockito.eq(namespace))).thenReturn(page); - Result result = k8sNameSpaceService.queryListPaging(getLoginUser(), namespace, 1, 10); + Result result = k8sNamespaceService.queryListPaging(getLoginUser(), namespace, 1, 10); logger.info(result.toString()); PageInfo pageInfo = (PageInfo) result.getData(); Assert.assertTrue(CollectionUtils.isNotEmpty(pageInfo.getTotalList())); @@ -95,19 +95,19 @@ public class K8sNameSpaceServiceTest { @Test public void createK8sNamespace() { // namespace is null - Map result = k8sNameSpaceService.createK8sNamespace(getLoginUser(), null, k8s, null, "tag", 10.0, 100); + Map result = k8sNamespaceService.createK8sNamespace(getLoginUser(), null, k8s, 10.0, 100); logger.info(result.toString()); Assert.assertEquals(Status.REQUEST_PARAMS_NOT_VALID_ERROR, result.get(Constants.STATUS)); // k8s is null - result = k8sNameSpaceService.createK8sNamespace(getLoginUser(), namespace, null, null, "tag", 10.0, 100); + result = k8sNamespaceService.createK8sNamespace(getLoginUser(), namespace, null, 10.0, 100); logger.info(result.toString()); Assert.assertEquals(Status.REQUEST_PARAMS_NOT_VALID_ERROR, result.get(Constants.STATUS)); // correct - result = k8sNameSpaceService.createK8sNamespace(getLoginUser(), namespace, k8s, null, "tag", 10.0, 100); + result = k8sNamespaceService.createK8sNamespace(getLoginUser(), namespace, k8s, 10.0, 100); logger.info(result.toString()); Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); //null limit cpu and mem - result = k8sNameSpaceService.createK8sNamespace(getLoginUser(), namespace, k8s, null, "tag", null, null); + result = k8sNamespaceService.createK8sNamespace(getLoginUser(), namespace, k8s, null, null); logger.info(result.toString()); Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); } @@ -116,15 +116,15 @@ public class K8sNameSpaceServiceTest { public void updateK8sNamespace() { Mockito.when(k8sNamespaceMapper.selectById(1)).thenReturn(getNamespace()); - Map result = k8sNameSpaceService.updateK8sNamespace(getLoginUser(), 1, null, "tag", null, null); + Map result = k8sNamespaceService.updateK8sNamespace(getLoginUser(), 1, null, null, null); logger.info(result.toString()); Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); - result = k8sNameSpaceService.updateK8sNamespace(getLoginUser(), 1, null, "tag", -1.0, 100); + result = k8sNamespaceService.updateK8sNamespace(getLoginUser(), 1, null, -1.0, 100); logger.info(result.toString()); Assert.assertEquals(Status.REQUEST_PARAMS_NOT_VALID_ERROR, result.get(Constants.STATUS)); - result = k8sNameSpaceService.updateK8sNamespace(getLoginUser(), 1, null, "tag", 1.0, 100); + result = k8sNamespaceService.updateK8sNamespace(getLoginUser(), 1, null, 1.0, 100); logger.info(result.toString()); Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); } @@ -135,22 +135,22 @@ public class K8sNameSpaceServiceTest { Mockito.when(k8sNamespaceMapper.existNamespace(namespace, k8s)).thenReturn(true); //namespace null - Result result = k8sNameSpaceService.verifyNamespaceK8s(null, k8s); + Result result = k8sNamespaceService.verifyNamespaceK8s(null, k8s); logger.info(result.toString()); Assert.assertEquals(result.getCode().intValue(), Status.REQUEST_PARAMS_NOT_VALID_ERROR.getCode()); //k8s null - result = k8sNameSpaceService.verifyNamespaceK8s(namespace, null); + result = k8sNamespaceService.verifyNamespaceK8s(namespace, null); logger.info(result.toString()); Assert.assertEquals(result.getCode().intValue(), Status.REQUEST_PARAMS_NOT_VALID_ERROR.getCode()); //exist - result = k8sNameSpaceService.verifyNamespaceK8s(namespace, k8s); + result = k8sNamespaceService.verifyNamespaceK8s(namespace, k8s); logger.info(result.toString()); Assert.assertEquals(result.getCode().intValue(), Status.K8S_NAMESPACE_EXIST.getCode()); //not exist - result = k8sNameSpaceService.verifyNamespaceK8s(namespace, "other k8s"); + result = k8sNamespaceService.verifyNamespaceK8s(namespace, "other k8s"); logger.info(result.toString()); Assert.assertEquals(result.getCode().intValue(), Status.SUCCESS.getCode()); } @@ -160,11 +160,57 @@ public class K8sNameSpaceServiceTest { Mockito.when(k8sNamespaceMapper.deleteById(Mockito.any())).thenReturn(1); Mockito.when(k8sNamespaceMapper.selectById(1)).thenReturn(getNamespace()); - Map result = k8sNameSpaceService.deleteNamespaceById(getLoginUser(), 1); + Map result = k8sNamespaceService.deleteNamespaceById(getLoginUser(), 1); logger.info(result.toString()); Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); } + @Test + public void testQueryAuthorizedNamespace() { + Mockito.when(k8sNamespaceMapper.queryAuthedNamespaceListByUserId(2)).thenReturn(getNamespaceList()); + + User loginUser = getLoginUser(); + + // test admin user + loginUser.setUserType(UserType.ADMIN_USER); + Map result = k8sNamespaceService.queryAuthorizedNamespace(loginUser, 2); + logger.info(result.toString()); + List namespaces = (List) result.get(Constants.DATA_LIST); + Assert.assertTrue(CollectionUtils.isNotEmpty(namespaces)); + + // test non-admin user + loginUser.setUserType(UserType.GENERAL_USER); + loginUser.setId(3); + result = k8sNamespaceService.queryAuthorizedNamespace(loginUser, 2); + Assert.assertEquals(Status.USER_NO_OPERATION_PERM, result.get(Constants.STATUS)); + namespaces = (List) result.get(Constants.DATA_LIST); + Assert.assertTrue(CollectionUtils.isEmpty(namespaces)); + } + + @Test + public void testQueryUnAuthorizedNamespace() { + Mockito.when(k8sNamespaceMapper.queryAuthedNamespaceListByUserId(2)).thenReturn(new ArrayList<>()); + Mockito.when(k8sNamespaceMapper.selectList(Mockito.any())).thenReturn(getNamespaceList()); + + // test admin user + User loginUser = new User(); + loginUser.setUserType(UserType.ADMIN_USER); + Map result = k8sNamespaceService.queryUnauthorizedNamespace(loginUser, 2); + logger.info(result.toString()); + List namespaces = (List) result.get(Constants.DATA_LIST); + Assert.assertTrue(CollectionUtils.isNotEmpty(namespaces)); + + // test non-admin user + loginUser.setId(2); + loginUser.setUserType(UserType.GENERAL_USER); + result = k8sNamespaceService.queryUnauthorizedNamespace(loginUser, 3); + logger.info(result.toString()); + Assert.assertEquals(Status.USER_NO_OPERATION_PERM, result.get(Constants.STATUS)); + namespaces = (List) result.get(Constants.DATA_LIST); + Assert.assertTrue(CollectionUtils.isEmpty(namespaces)); + } + + private User getLoginUser() { User loginUser = new User(); diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java index 596eba4d75..8eaba04c60 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java @@ -90,6 +90,9 @@ public class UsersServiceTest { @Mock private UDFUserMapper udfUserMapper; + @Mock + private K8sNamespaceUserMapper k8sNamespaceUserMapper; + @Mock private ProjectMapper projectMapper; @@ -434,6 +437,24 @@ public class UsersServiceTest { Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); } + @Test + public void testGrantNamespaces() { + String namespaceIds = "100000,120000"; + when(userMapper.selectById(1)).thenReturn(getUser()); + User loginUser = new User(); + + //user not exist + loginUser.setUserType(UserType.ADMIN_USER); + Map result = usersService.grantNamespaces(loginUser, 2, namespaceIds); + logger.info(result.toString()); + Assert.assertEquals(Status.USER_NOT_EXIST, result.get(Constants.STATUS)); + //success + when(k8sNamespaceUserMapper.deleteNamespaceRelation(0,1)).thenReturn(1); + result = usersService.grantNamespaces(loginUser, 1, namespaceIds); + logger.info(result.toString()); + Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); + } + @Test public void testGrantDataSource() { String datasourceIds = "100000,120000"; diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/Constants.java b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/Constants.java index 4199627f71..3d87af5ea8 100644 --- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/Constants.java +++ b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/Constants.java @@ -816,4 +816,5 @@ public final class Constants { public static final String K8S = "k8s"; public static final String LIMITS_CPU = "limitsCpu"; public static final String LIMITS_MEMORY = "limitsMemory"; + public static final String K8S_LOCAL_TEST_CLUSTER = "ds_null_k8s"; } diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/K8sNamespace.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/K8sNamespace.java index d22e7bd520..e6864a58bd 100644 --- a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/K8sNamespace.java +++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/K8sNamespace.java @@ -37,54 +37,66 @@ public class K8sNamespace { */ @TableField(value = "namespace") private String namespace; + /** * total cpu limit */ @TableField(value = "limits_cpu") private Double limitsCpu; + /** * total memory limit,mi */ private Integer limitsMemory; + /** * owner */ - @TableField(value = "owner") - private String owner; + @TableField(value = "user_id") + private int userId; + + /** + * user name + */ + @TableField(exist = false) + private String userName; /** * create_time */ @TableField("create_time") private Date createTime; + /** * update_time */ @TableField("update_time") private Date updateTime; + /** - * tag use for set this namespace allow run which type + * 1.00 = 1 cpu */ - @TableField("tag") - private String tag; - @TableField("pod_request_cpu") private Double podRequestCpu = 0.0; + /** * Mi */ @TableField("pod_request_memory") private Integer podRequestMemory = 0; + /** - * + * replicas */ @TableField("pod_replicas") private Integer podReplicas = 0; + /** * online job */ @TableField("online_job_num") private Integer onlineJobNum = 0; + /** * k8s name */ @@ -123,12 +135,12 @@ public class K8sNamespace { this.limitsMemory = limitsMemory; } - public String getOwner() { - return owner; + public int getUserId() { + return userId; } - public void setOwner(String owner) { - this.owner = owner; + public void setUserId(int userId) { + this.userId = userId; } public Date getCreateTime() { @@ -147,14 +159,6 @@ public class K8sNamespace { this.updateTime = updateTime; } - public String getTag() { - return tag; - } - - public void setTag(String tag) { - this.tag = tag; - } - public Integer getPodRequestMemory() { return podRequestMemory; } @@ -194,4 +198,54 @@ public class K8sNamespace { public void setPodRequestCpu(Double podRequestCpu) { this.podRequestCpu = podRequestCpu; } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + @Override + public String toString() { + return "K8sNamespace{" + + "id=" + id + + ", namespace=" + namespace + + ", limitsCpu=" + limitsCpu + + ", limitsMemory=" + limitsMemory + + ", userId=" + userId + + ", podRequestCpu=" + podRequestCpu + + ", podRequestMemory=" + podRequestMemory + + ", podReplicas=" + podReplicas + + ", k8s=" + k8s + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + K8sNamespace k8sNamespace = (K8sNamespace) o; + + if (id.equals(k8sNamespace.id)) { + return true; + } + + return namespace.equals(k8sNamespace.namespace) && k8s.equals(k8sNamespace.k8s); + } + + @Override + public int hashCode() { + int result = id; + result = 31 * result + (k8s+namespace).hashCode(); + return result; + } } diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/K8sNamespaceUser.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/K8sNamespaceUser.java new file mode 100644 index 0000000000..87a7a3ecc4 --- /dev/null +++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/K8sNamespaceUser.java @@ -0,0 +1,164 @@ +/* + * 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.apache.dolphinscheduler.dao.entity; + +import java.util.Date; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +/** + * k8s namespace and user relation + */ +@TableName("t_ds_relation_namespace_user") +public class K8sNamespaceUser { + /** + * id + */ + @TableId(value = "id", type = IdType.AUTO) + private int id; + + /** + * user id + */ + @TableField("user_id") + private int userId; + + /** + * namespace id + */ + @TableField("namespace_id") + private int namespaceId; + + /** + * k8s cluster + */ + @TableField(exist = false) + private String k8s; + + /** + * namespace name + */ + @TableField(exist = false) + private String namespaceName; + + /** + * user name + */ + @TableField(exist = false) + private String userName; + + /** + * permission + */ + private int perm; + + @TableField("create_time") + private Date createTime; + + @TableField("update_time") + private Date updateTime; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public int getNamespaceId() { + return namespaceId; + } + + public void setNamespaceId(int namespaceId) { + this.namespaceId = namespaceId; + } + + public String getK8s() { + return k8s; + } + + public void setK8s(String k8s) { + this.k8s = k8s; + } + + public String getNamespaceName() { + return namespaceName; + } + + public void setNamespaceName(String namespaceName) { + this.namespaceName = namespaceName; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public int getPerm() { + return perm; + } + + public void setPerm(int perm) { + this.perm = perm; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String toString() { + return "K8sNamespaceUser{" + + "id=" + id + + ", userId=" + userId + + ", namespaceId=" + namespaceId + + ", k8s=" + k8s + + ", namespaceName=" + namespaceName + + ", perm=" + perm + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + '}'; + } +} diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapper.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapper.java index e804d55402..e8f80ebf3f 100644 --- a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapper.java +++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapper.java @@ -21,6 +21,8 @@ import org.apache.dolphinscheduler.dao.entity.K8sNamespace; import org.apache.ibatis.annotations.Param; +import java.util.List; + import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; @@ -46,4 +48,28 @@ public interface K8sNamespaceMapper extends BaseMapper { * @return true if exist else return null */ Boolean existNamespace(@Param("namespace") String namespace, @Param("k8s") String k8s); + + /** + * query namespace except userId + * + * @param userId userId + * @return namespace list + */ + List queryNamespaceExceptUserId(@Param("userId") int userId); + + /** + * query authed namespace list by userId + * + * @param userId userId + * @return namespace list + */ + List queryAuthedNamespaceListByUserId(@Param("userId") int userId); + + /** + * query namespace can use + * + * @param userId userId + * @return namespace list + */ + List queryNamespaceAvailable(@Param("userId") Integer userId); } diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceUserMapper.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceUserMapper.java new file mode 100644 index 0000000000..46723cc41d --- /dev/null +++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceUserMapper.java @@ -0,0 +1,50 @@ +/* + * 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.apache.dolphinscheduler.dao.mapper; + +import org.apache.dolphinscheduler.dao.entity.K8sNamespaceUser; + +import org.apache.ibatis.annotations.Param; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * namespace user mapper interface + */ +public interface K8sNamespaceUserMapper extends BaseMapper { + + /** + * delete namespace user relation + * + * @param namespaceId namespaceId + * @param userId userId + * @return delete result + */ + int deleteNamespaceRelation(@Param("namespaceId") int namespaceId, + @Param("userId") int userId); + + /** + * query namespace relation + * + * @param namespaceId namespaceId + * @param userId userId + * @return namespace user relation + */ + K8sNamespaceUser queryNamespaceRelation(@Param("namespaceId") int namespaceId, + @Param("userId") int userId); +} diff --git a/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapper.xml b/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapper.xml index 9099e0c411..ba32fc2b12 100644 --- a/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapper.xml +++ b/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceMapper.xml @@ -20,8 +20,13 @@ - id, namespace, k8s, owner, tag, limits_memory, limits_cpu, online_job_num, pod_replicas, pod_request_cpu, pod_request_memory, create_time, update_time + id, namespace, k8s, user_id, limits_memory, limits_cpu, online_job_num, pod_replicas, pod_request_cpu, pod_request_memory, create_time, update_time + + + ${alias}.id, ${alias}.namespace, ${alias}.k8s, ${alias}.user_id, ${alias}.limits_memory, ${alias}.limits_cpu, ${alias}.online_job_num, ${alias}.pod_replicas, ${alias}.pod_request_cpu, ${alias}.pod_request_memory, ${alias}.create_time, ${alias}.update_time + + + + + + + diff --git a/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceUserMapper.xml b/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceUserMapper.xml new file mode 100644 index 0000000000..7e26039b99 --- /dev/null +++ b/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/K8sNamespaceUserMapper.xml @@ -0,0 +1,40 @@ + + + + + + + id, user_id, namespace_id, perm, create_time, update_time + + + delete from t_ds_relation_namespace_user + where 1=1 + and user_id = #{userId} + + and namespace_id = #{namespaceId} + + + + \ No newline at end of file diff --git a/dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_h2.sql b/dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_h2.sql index 7fd9dc8256..f6cfae00e0 100644 --- a/dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_h2.sql +++ b/dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_h2.sql @@ -1898,11 +1898,10 @@ CREATE TABLE t_ds_k8s_namespace ( limits_memory int(11) DEFAULT NULL, namespace varchar(100) DEFAULT NULL, online_job_num int(11) DEFAULT NULL, - owner varchar(100) DEFAULT NULL, + user_id int(11) DEFAULT NULL, pod_replicas int(11) DEFAULT NULL, pod_request_cpu decimal(14,3) DEFAULT NULL, pod_request_memory int(11) DEFAULT NULL, - tag varchar(100) DEFAULT NULL, limits_cpu decimal(14,3) DEFAULT NULL, k8s varchar(100) DEFAULT NULL, create_time datetime DEFAULT NULL , @@ -1914,8 +1913,32 @@ CREATE TABLE t_ds_k8s_namespace ( -- ---------------------------- -- Records of t_ds_k8s_namespace -- ---------------------------- -INSERT INTO t_ds_k8s_namespace -VALUES (1, 10000, 'default', 99, 'owner',1,NULL,1,'test',NULL,'default',null,null); +INSERT INTO `t_ds_k8s_namespace` +(`id`,`limits_memory`,`namespace`,`online_job_num`,`user_id`,`pod_replicas`,`pod_request_cpu`,`pod_request_memory`,`limits_cpu`,`k8s`,`create_time`,`update_time`) +VALUES (1, 1000, 'flink_test', 99, 1, 1, 0.1, 1, NULL, 'ds_null_k8s', '2022-03-03 11:31:24.0', '2022-03-03 11:31:24.0'); + +INSERT INTO `t_ds_k8s_namespace` +(`id`,`limits_memory`,`namespace`,`online_job_num`,`user_id`,`pod_replicas`,`pod_request_cpu`,`pod_request_memory`,`limits_cpu`,`k8s`,`create_time`,`update_time`) +VALUES (2, 500, 'spark_test', 90, 2,1,10000,1, NULL, 'ds_null_k8s', '2021-03-03 11:31:24.0', '2021-03-03 11:31:24.0'); + +INSERT INTO `t_ds_k8s_namespace` +(`id`,`limits_memory`,`namespace`,`online_job_num`,`user_id`,`pod_replicas`,`pod_request_cpu`,`pod_request_memory`,`limits_cpu`,`k8s`,`create_time`,`update_time`) +VALUES (3, 200, 'auth_test', 68, 3,1,100,1, 10000, 'ds_null_k8s', '2020-03-03 11:31:24.0', '2020-03-03 11:31:24.0'); + +-- ---------------------------- +-- Table structure for t_ds_relation_namespace_user +-- ---------------------------- +DROP TABLE IF EXISTS t_ds_relation_namespace_user; +CREATE TABLE t_ds_relation_namespace_user ( + id int(11) NOT NULL AUTO_INCREMENT , + user_id int(11) NOT NULL , + namespace_id int(11) NOT NULL , + perm int(11) DEFAULT '1' , + create_time datetime DEFAULT NULL , + update_time datetime DEFAULT NULL , + PRIMARY KEY (id) , + UNIQUE KEY namespace_user_unique (user_id,namespace_id) +); -- ---------------------------- -- Table structure for t_ds_alert_send_status diff --git a/dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_mysql.sql b/dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_mysql.sql index 051ea1d006..197173a1b8 100644 --- a/dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_mysql.sql +++ b/dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_mysql.sql @@ -1891,11 +1891,10 @@ CREATE TABLE `t_ds_k8s_namespace` ( `limits_memory` int(11) DEFAULT NULL, `namespace` varchar(100) DEFAULT NULL, `online_job_num` int(11) DEFAULT NULL, - `owner` varchar(100) DEFAULT NULL, + `user_id` int(11) DEFAULT NULL, `pod_replicas` int(11) DEFAULT NULL, `pod_request_cpu` decimal(14,3) DEFAULT NULL, `pod_request_memory` int(11) DEFAULT NULL, - `tag` varchar(100) DEFAULT NULL, `limits_cpu` decimal(14,3) DEFAULT NULL, `k8s` varchar(100) DEFAULT NULL, `create_time` datetime DEFAULT NULL COMMENT 'create time', @@ -1904,6 +1903,20 @@ CREATE TABLE `t_ds_k8s_namespace` ( UNIQUE KEY `k8s_namespace_unique` (`namespace`,`k8s`) ) ENGINE= INNODB AUTO_INCREMENT= 1 DEFAULT CHARSET= utf8; +-- ---------------------------- +-- Table structure for t_ds_relation_namespace_user +-- ---------------------------- +DROP TABLE IF EXISTS `t_ds_relation_namespace_user`; +CREATE TABLE `t_ds_relation_namespace_user` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'key', + `user_id` int(11) NOT NULL COMMENT 'user id', + `namespace_id` int(11) DEFAULT NULL COMMENT 'namespace id', + `perm` int(11) DEFAULT '1' COMMENT 'limits of authority', + `create_time` datetime DEFAULT NULL COMMENT 'create time', + `update_time` datetime DEFAULT NULL COMMENT 'update time', + PRIMARY KEY (`id`), + UNIQUE KEY `namespace_user_unique` (`user_id`,`namespace_id`) +) ENGINE=InnoDB AUTO_INCREMENT= 1 DEFAULT CHARSET= utf8; -- ---------------------------- -- Table structure for t_ds_alert_send_status diff --git a/dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_postgresql.sql b/dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_postgresql.sql index 9e9c64d089..7e5526976f 100644 --- a/dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_postgresql.sql +++ b/dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_postgresql.sql @@ -1889,11 +1889,10 @@ CREATE TABLE t_ds_k8s_namespace ( limits_memory int DEFAULT NULL , namespace varchar(100) DEFAULT NULL , online_job_num int DEFAULT '0' , - owner varchar(100) DEFAULT NULL, + user_id int DEFAULT NULL, pod_replicas int DEFAULT NULL, pod_request_cpu NUMERIC(13,4) NULL, pod_request_memory int DEFAULT NULL, - tag varchar(100) DEFAULT NULL, limits_cpu NUMERIC(13,4) NULL, k8s varchar(100) DEFAULT NULL, create_time timestamp DEFAULT NULL , @@ -1902,6 +1901,22 @@ CREATE TABLE t_ds_k8s_namespace ( CONSTRAINT k8s_namespace_unique UNIQUE (namespace,k8s) ); +-- +-- Table structure for table t_ds_relation_namespace_user +-- + +DROP TABLE IF EXISTS t_ds_relation_namespace_user; +CREATE TABLE t_ds_relation_namespace_user ( + id serial NOT NULL, + user_id int DEFAULT NULL , + namespace_id int DEFAULT NULL , + perm int DEFAULT NULL , + create_time timestamp DEFAULT NULL , + update_time timestamp DEFAULT NULL , + PRIMARY KEY (id) , + CONSTRAINT namespace_user_unique UNIQUE (user_id,namespace_id) +); + -- ---------------------------- -- Table structure for t_ds_alert_send_status -- ---------------------------- diff --git a/dolphinscheduler-dao/src/main/resources/sql/upgrade/2.1.0_schema/mysql/dolphinscheduler_ddl.sql b/dolphinscheduler-dao/src/main/resources/sql/upgrade/2.1.0_schema/mysql/dolphinscheduler_ddl.sql index ac08027dd6..4f0dacaf9d 100644 --- a/dolphinscheduler-dao/src/main/resources/sql/upgrade/2.1.0_schema/mysql/dolphinscheduler_ddl.sql +++ b/dolphinscheduler-dao/src/main/resources/sql/upgrade/2.1.0_schema/mysql/dolphinscheduler_ddl.sql @@ -199,11 +199,10 @@ CREATE TABLE `t_ds_k8s_namespace` ( `limits_memory` int(11) DEFAULT NULL, `namespace` varchar(100) DEFAULT NULL, `online_job_num` int(11) DEFAULT NULL, - `owner` varchar(100) DEFAULT NULL, + `user_id` int(11) DEFAULT NULL, `pod_replicas` int(11) DEFAULT NULL, `pod_request_cpu` decimal(14,3) DEFAULT NULL, `pod_request_memory` int(11) DEFAULT NULL, - `tag` varchar(100) DEFAULT NULL, `limits_cpu` decimal(14,3) DEFAULT NULL, `k8s` varchar(100) DEFAULT NULL, `create_time` datetime DEFAULT NULL COMMENT 'create time', @@ -211,3 +210,19 @@ CREATE TABLE `t_ds_k8s_namespace` ( PRIMARY KEY (`id`), UNIQUE KEY `k8s_namespace_unique` (`namespace`,`k8s`) ) ENGINE= INNODB AUTO_INCREMENT= 1 DEFAULT CHARSET= utf8; + +-- ---------------------------- +-- Table structure for t_ds_relation_namespace_user +-- ---------------------------- +DROP TABLE IF EXISTS `t_ds_relation_namespace_user`; +CREATE TABLE `t_ds_relation_namespace_user` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'key', + `user_id` int(11) NOT NULL COMMENT 'user id', + `namespace_id` int(11) DEFAULT NULL COMMENT 'namespace id', + `perm` int(11) DEFAULT '1' COMMENT 'limits of authority', + `create_time` datetime DEFAULT NULL COMMENT 'create time', + `update_time` datetime DEFAULT NULL COMMENT 'update time', + PRIMARY KEY (`id`), + KEY `user_id_index` (`user_id`), + UNIQUE KEY `namespace_user_unique` (`user_id`,`namespace_id`) +) ENGINE=InnoDB AUTO_INCREMENT= 1 DEFAULT CHARSET= utf8; \ No newline at end of file diff --git a/dolphinscheduler-dao/src/main/resources/sql/upgrade/2.1.0_schema/postgresql/dolphinscheduler_ddl.sql b/dolphinscheduler-dao/src/main/resources/sql/upgrade/2.1.0_schema/postgresql/dolphinscheduler_ddl.sql index 9e2459f82d..e3d7b8c568 100644 --- a/dolphinscheduler-dao/src/main/resources/sql/upgrade/2.1.0_schema/postgresql/dolphinscheduler_ddl.sql +++ b/dolphinscheduler-dao/src/main/resources/sql/upgrade/2.1.0_schema/postgresql/dolphinscheduler_ddl.sql @@ -180,12 +180,11 @@ EXECUTE 'CREATE TABLE IF NOT EXISTS '|| quote_ident(v_schema) ||'."t_ds_k8s_name id serial NOT NULL, limits_memory int DEFAULT NULL , namespace varchar(100) DEFAULT NULL , - online_job_num int DEFAULT ''0'' , - owner varchar(100) DEFAULT NULL, + online_job_num int DEFAULT NULL, + user_id int DEFAULT NULL, pod_replicas int DEFAULT NULL, pod_request_cpu NUMERIC(13,4) NULL, pod_request_memory int DEFAULT NULL, - tag varchar(100) DEFAULT NULL, limits_cpu NUMERIC(13,4) NULL, k8s varchar(100) DEFAULT NULL, create_time timestamp DEFAULT NULL , @@ -194,6 +193,17 @@ EXECUTE 'CREATE TABLE IF NOT EXISTS '|| quote_ident(v_schema) ||'."t_ds_k8s_name CONSTRAINT k8s_namespace_unique UNIQUE (namespace,k8s) )'; +EXECUTE 'CREATE TABLE IF NOT EXISTS '|| quote_ident(v_schema) ||'."t_ds_relation_namespace_user" ( + id serial NOT NULL, + user_id int DEFAULT NULL , + namespace_id int DEFAULT NULL , + perm int DEFAULT NULL , + create_time timestamp DEFAULT NULL , + update_time timestamp DEFAULT NULL , + PRIMARY KEY (id) , + CONSTRAINT namespace_user_unique UNIQUE (user_id,namespace_id) +)'; + return 'Success!'; exception when others then ---Raise EXCEPTION '(%)',SQLERRM; diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts index 1f00751a79..c785a70170 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts @@ -1045,8 +1045,10 @@ const security = { udf_resource: 'UDF Resource', datasource: 'Datasource', udf: 'UDF Function', + namespace: 'Namespace', authorize_project: 'Project Authorize', authorize_resource: 'Resource Authorize', + authorize_namespace: 'Namespace Authorize', authorize_datasource: 'Datasource Authorize', authorize_udf: 'UDF Function Authorize', username: 'Username', diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts index e590d67524..c8a3d47aa7 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts @@ -1032,8 +1032,10 @@ const security = { udf_resource: 'UDF资源', datasource: '数据源', udf: 'UDF函数', + namespace: '命名空间', authorize_project: '项目授权', authorize_resource: '资源授权', + authorize_namespace: '命名空间授权', authorize_datasource: '数据源授权', authorize_udf: 'UDF函数授权', username: '用户名', diff --git a/dolphinscheduler-ui-next/src/service/modules/k8s-namespace/index.ts b/dolphinscheduler-ui-next/src/service/modules/k8s-namespace/index.ts index 69aaae9684..2df1d5f703 100644 --- a/dolphinscheduler-ui-next/src/service/modules/k8s-namespace/index.ts +++ b/dolphinscheduler-ui-next/src/service/modules/k8s-namespace/index.ts @@ -17,6 +17,7 @@ import { axios } from '@/service/service' import { ListReq, K8SReq } from './types' +import { UserIdReq } from '@/service/modules/resources/types' export function queryNamespaceListPaging(params: ListReq): any { return axios({ @@ -60,3 +61,19 @@ export function delNamespaceById(id: number): any { params: { id } }) } + +export function authNamespaceFunc(params: UserIdReq): any { + return axios({ + url: '/k8s-namespace/authed-namespace', + method: 'get', + params + }) +} + +export function unAuthNamespaceFunc(params: UserIdReq): any { + return axios({ + url: '/k8s-namespace/unauth-namespace', + method: 'get', + params + }) +} diff --git a/dolphinscheduler-ui-next/src/service/modules/users/index.ts b/dolphinscheduler-ui-next/src/service/modules/users/index.ts index bd42f40a65..9a1313cbfb 100644 --- a/dolphinscheduler-ui-next/src/service/modules/users/index.ts +++ b/dolphinscheduler-ui-next/src/service/modules/users/index.ts @@ -28,6 +28,7 @@ import { GrantProject, ProjectCodeReq, GrantUDFReq, + GrantNamespaceReq, ListAllReq, ListReq, RegisterUserReq @@ -120,6 +121,14 @@ export function grantUDFFunc(data: GrantUDFReq & UserIdReq) { }) } +export function grantNamespaceFunc(data: GrantNamespaceReq & UserIdReq) { + return axios({ + url: '/users/grant-namespace', + method: 'post', + data + }) +} + export function listUser(): any { return axios({ url: '/users/list', diff --git a/dolphinscheduler-ui-next/src/service/modules/users/types.ts b/dolphinscheduler-ui-next/src/service/modules/users/types.ts index 3768d22b14..a49370560c 100644 --- a/dolphinscheduler-ui-next/src/service/modules/users/types.ts +++ b/dolphinscheduler-ui-next/src/service/modules/users/types.ts @@ -66,6 +66,10 @@ interface GrantUDFReq { udfIds: string } +interface GrantNamespaceReq { + namespaceIds: string +} + interface ListAllReq extends UserReq { alertGroup?: string createTime?: string @@ -127,6 +131,7 @@ export { GrantProject, ProjectCodeReq, GrantUDFReq, + GrantNamespaceReq, ListAllReq, ListReq, RegisterUserReq, diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/components/authorize-modal.tsx b/dolphinscheduler-ui-next/src/views/security/user-manage/components/authorize-modal.tsx index 0118299f40..bce9c567c6 100644 --- a/dolphinscheduler-ui-next/src/views/security/user-manage/components/authorize-modal.tsx +++ b/dolphinscheduler-ui-next/src/views/security/user-manage/components/authorize-modal.tsx @@ -151,6 +151,15 @@ export const AuthorizeModal = defineComponent({ /> )} + {type === 'authorize_namespace' && ( + + )} ) } diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/components/use-authorize.ts b/dolphinscheduler-ui-next/src/views/security/user-manage/components/use-authorize.ts index f12b6a3219..819a7a444f 100644 --- a/dolphinscheduler-ui-next/src/views/security/user-manage/components/use-authorize.ts +++ b/dolphinscheduler-ui-next/src/views/security/user-manage/components/use-authorize.ts @@ -29,11 +29,16 @@ import { authUDFFunc, unAuthUDFFunc } from '@/service/modules/resources' +import { + authNamespaceFunc, + unAuthNamespaceFunc +} from '@/service/modules/k8s-namespace' import { grantProject, grantResource, grantDataSource, - grantUDFFunc + grantUDFFunc, + grantNamespaceFunc } from '@/service/modules/users' import { removeUselessChildren } from '@/utils/tree-format' import type { TAuthType, IResourceOption, IOption } from '../types' @@ -48,6 +53,8 @@ export function useAuthorize() { unauthorizedDatasources: [] as IOption[], authorizedUdfs: [] as number[], unauthorizedUdfs: [] as IOption[], + authorizedNamespaces: [] as number[], + unauthorizedNamespaces: [] as IOption[], resourceType: 'file', fileResources: [] as IResourceOption[], udfResources: [] as IResourceOption[], @@ -139,6 +146,25 @@ export function useAuthorize() { state.authorizedUdfResources = udfTargets } + const getNamespaces = async (userId: number) => { + if (state.loading) return + state.loading = true + const namespaces = await Promise.all([ + authNamespaceFunc({ userId }), + unAuthNamespaceFunc({ userId }) + ]) + state.loading = false + state.authorizedNamespaces = namespaces[0].map( + (item: { id: number }) => item.id + ) + state.unauthorizedNamespaces = [...namespaces[0], ...namespaces[1]].map( + (item: { namespace: string; id: number }) => ({ + label: item.namespace, + value: item.id + }) + ) + } + const onInit = (type: TAuthType, userId: number) => { if (type === 'authorize_project') { getProjects(userId) @@ -152,6 +178,9 @@ export function useAuthorize() { if (type === 'authorize_resource') { getResources(userId) } + if (type === 'authorize_namespace') { + getNamespaces(userId) + } } /* @@ -242,6 +271,12 @@ export function useAuthorize() { resourceIds: allPathId.join(',') }) } + if (type === 'authorize_namespace') { + await grantNamespaceFunc({ + userId, + namespaceIds: state.authorizedNamespaces.join(',') + }) + } state.saving = false return true } diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/types.ts b/dolphinscheduler-ui-next/src/views/security/user-manage/types.ts index ac8c1fc8a6..8c98d222fa 100644 --- a/dolphinscheduler-ui-next/src/views/security/user-manage/types.ts +++ b/dolphinscheduler-ui-next/src/views/security/user-manage/types.ts @@ -27,6 +27,7 @@ type TAuthType = | 'authorize_resource' | 'authorize_datasource' | 'authorize_udf' + | 'authorize_namespace' interface IRecord { id: number diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/use-columns.ts b/dolphinscheduler-ui-next/src/views/security/user-manage/use-columns.ts index 288b2b6343..f0af7aec9c 100644 --- a/dolphinscheduler-ui-next/src/views/security/user-manage/use-columns.ts +++ b/dolphinscheduler-ui-next/src/views/security/user-manage/use-columns.ts @@ -137,7 +137,11 @@ export function useColumns(onCallback: Function) { label: t('security.user.datasource'), key: 'authorize_datasource' }, - { label: t('security.user.udf'), key: 'authorize_udf' } + { label: t('security.user.udf'), key: 'authorize_udf' }, + { + label: t('security.user.namespace'), + key: 'authorize_namespace' + } ], onSelect: (key) => void onCallback({ rowData, key }, 'authorize') diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/namespace/_source/createNamespace.vue b/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/namespace/_source/createNamespace.vue index 3d0903e85d..42e2459307 100644 --- a/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/namespace/_source/createNamespace.vue +++ b/dolphinscheduler-ui/src/js/conf/home/pages/security/pages/namespace/_source/createNamespace.vue @@ -48,18 +48,6 @@ - - - - @@ -90,7 +78,7 @@