You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dolphinscheduler.apache.org by zi...@apache.org on 2022/10/27 10:02:00 UTC

[dolphinscheduler] branch dev updated: [Feature-12040][api][ui] Add authorization management of read and write permissions for project center (#12048)

This is an automated email from the ASF dual-hosted git repository.

zixi0825 pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/dolphinscheduler.git


The following commit(s) were added to refs/heads/dev by this push:
     new dc8d18cf87 [Feature-12040][api][ui] Add authorization management of read and write permissions for project center (#12048)
dc8d18cf87 is described below

commit dc8d18cf871c9c54f2a7e10023469428bc19f983
Author: Yiming Guo <49...@users.noreply.github.com>
AuthorDate: Thu Oct 27 18:01:51 2022 +0800

    [Feature-12040][api][ui] Add authorization management of read and write permissions for project center (#12048)
    
    [Feature-12040][api][ui] Add authorization management of read and write permissions for project center
---
 .../api/controller/ProjectController.java          |  57 ++++++++
 .../api/controller/UsersController.java            |  48 +++++++
 .../apache/dolphinscheduler/api/enums/Status.java  |   2 +
 .../ResourcePermissionCheckServiceImpl.java        |   2 +-
 .../api/service/ProjectService.java                |  24 ++++
 .../api/service/ResourcesService.java              |   4 +-
 .../dolphinscheduler/api/service/UsersService.java |  19 +++
 .../service/impl/ProcessDefinitionServiceImpl.java |  28 ++--
 .../api/service/impl/ProjectServiceImpl.java       | 156 ++++++++++++++++++++-
 .../service/impl/TaskDefinitionServiceImpl.java    |  33 +++--
 .../api/service/impl/UsersServiceImpl.java         |  97 ++++++++++++-
 .../api/service/ProcessDefinitionServiceTest.java  |   1 +
 .../api/service/ProjectServiceTest.java            | 106 +++++++++++++-
 .../api/service/TaskDefinitionServiceImplTest.java |   8 +-
 .../api/service/UsersServiceTest.java              |  46 ++++++
 .../dao/mapper/ProjectUserMapper.java              |   2 +-
 .../src/common/column-width-config.ts              |   3 +
 dolphinscheduler-ui/src/locales/en_US/project.ts   |   6 +-
 dolphinscheduler-ui/src/locales/en_US/security.ts  |   3 +
 dolphinscheduler-ui/src/locales/zh_CN/project.ts   |   6 +-
 dolphinscheduler-ui/src/locales/zh_CN/security.ts  |   3 +
 .../src/service/modules/projects/index.ts          |  18 ++-
 .../src/service/modules/projects/types.ts          |   8 ++
 .../src/service/modules/users/index.ts             |  16 +++
 .../user-manage/components/authorize-modal.tsx     |  94 +++++++++++--
 .../user-manage/components/use-authorize.ts        |  91 ++++++++----
 .../security/user-manage/components/use-columns.ts |  91 ++++++++++++
 .../views/security/user-manage/index.module.scss   |   5 +
 28 files changed, 899 insertions(+), 78 deletions(-)

diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/ProjectController.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/ProjectController.java
index 1b3f2def46..dcfc774b4c 100644
--- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/ProjectController.java
+++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/ProjectController.java
@@ -174,6 +174,43 @@ public class ProjectController extends BaseController {
         return result;
     }
 
+    /**
+     * query project with authorized level list paging
+     *
+     * @param userId user id
+     * @param loginUser login user
+     * @param searchVal search value
+     * @param pageSize page size
+     * @param pageNo page number
+     * @return project list which with the login user's authorized level
+     */
+    @Operation(summary = "queryProjectWithAuthorizedLevelListPaging", description = "QUERY_PROJECT_WITH_AUTH_LEVEL_LIST_PAGING_NOTES")
+    @Parameters({
+            @Parameter(name = "userId", description = "USER_ID", schema = @Schema(implementation = int.class, example = "100")),
+            @Parameter(name = "searchVal", description = "SEARCH_VAL", schema = @Schema(implementation = String.class)),
+            @Parameter(name = "pageSize", description = "PAGE_SIZE", required = true, schema = @Schema(implementation = int.class, example = "10")),
+            @Parameter(name = "pageNo", description = "PAGE_NO", required = true, schema = @Schema(implementation = int.class, example = "1"))
+    })
+    @GetMapping(value = "/project-with-authorized-level-list-paging")
+    @ResponseStatus(HttpStatus.OK)
+    @ApiException(LOGIN_USER_QUERY_PROJECT_LIST_PAGING_ERROR)
+    @AccessLogAnnotation(ignoreRequestArgs = "loginUser")
+    public Result queryProjectWithAuthorizedLevelListPaging(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                                            @RequestParam("userId") Integer userId,
+                                                            @RequestParam(value = "searchVal", required = false) String searchVal,
+                                                            @RequestParam("pageSize") Integer pageSize,
+                                                            @RequestParam("pageNo") Integer pageNo) {
+
+        Result result = checkPageParams(pageNo, pageSize);
+        if (!result.checkResult()) {
+            return result;
+        }
+        searchVal = ParameterUtils.handleEscapes(searchVal);
+        result = projectService.queryProjectWithAuthorizedLevelListPaging(userId, loginUser, pageSize, pageNo,
+                searchVal);
+        return result;
+    }
+
     /**
      * delete project by code
      *
@@ -234,6 +271,26 @@ public class ProjectController extends BaseController {
         return projectService.queryAuthorizedProject(loginUser, userId);
     }
 
+    /**
+     * query all project with authorized level
+     *
+     * @param loginUser login user
+     * @param userId user id
+     * @return All projects with users' authorized level for them
+     */
+    @Operation(summary = "queryProjectWithAuthorizedLevel", description = "QUERY_PROJECT_AUTHORIZED_LEVEL")
+    @Parameters({
+            @Parameter(name = "userId", description = "USER_ID", schema = @Schema(implementation = int.class, example = "100"))
+    })
+    @GetMapping(value = "/project-with-authorized-level")
+    @ResponseStatus(HttpStatus.OK)
+    @ApiException(QUERY_AUTHORIZED_PROJECT)
+    @AccessLogAnnotation(ignoreRequestArgs = "loginUser")
+    public Result queryProjectWithAuthorizedLevel(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                                  @RequestParam("userId") Integer userId) {
+        return projectService.queryProjectWithAuthorizedLevel(loginUser, userId);
+    }
+
     /**
      * query authorized user
      *
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 dcaf7a9b81..93c13b6f50 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
@@ -214,6 +214,54 @@ public class UsersController extends BaseController {
         return returnDataList(result);
     }
 
+    /**
+     * revoke project By Id
+     *
+     * @param loginUser login user
+     * @param userId user id
+     * @param projectIds project id array
+     * @return revoke result code
+     */
+    @Operation(summary = "revokeProjectById", description = "REVOKE_PROJECT_NOTES")
+    @Parameters({
+            @Parameter(name = "userId", description = "USER_ID", required = true, schema = @Schema(implementation = int.class, example = "100")),
+            @Parameter(name = "projectIds", description = "PROJECT_IDS", required = true, schema = @Schema(implementation = String.class))
+    })
+    @PostMapping(value = "/revoke-project-by-id")
+    @ResponseStatus(HttpStatus.OK)
+    @ApiException(REVOKE_PROJECT_ERROR)
+    @AccessLogAnnotation
+    public Result revokeProjectById(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                    @RequestParam(value = "userId") int userId,
+                                    @RequestParam(value = "projectIds") String projectIds) {
+        Map<String, Object> result = usersService.revokeProjectById(loginUser, userId, projectIds);
+        return returnDataList(result);
+    }
+
+    /**
+     * grant project with read permission
+     *
+     * @param loginUser login user
+     * @param userId user id
+     * @param projectIds project id array
+     * @return grant result code
+     */
+    @Operation(summary = "grantProjectWithReadPerm", description = "GRANT_PROJECT_WITH_READ_PERM_NOTES")
+    @Parameters({
+            @Parameter(name = "userId", description = "USER_ID", required = true, schema = @Schema(implementation = int.class, example = "100")),
+            @Parameter(name = "projectIds", description = "PROJECT_IDS", required = true, schema = @Schema(implementation = String.class))
+    })
+    @PostMapping(value = "/grant-project-with-read-perm")
+    @ResponseStatus(HttpStatus.OK)
+    @ApiException(GRANT_PROJECT_ERROR)
+    @AccessLogAnnotation
+    public Result grantProjectWithReadPerm(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                           @RequestParam(value = "userId") int userId,
+                                           @RequestParam(value = "projectIds") String projectIds) {
+        Map<String, Object> result = usersService.grantProjectWithReadPerm(loginUser, userId, projectIds);
+        return returnDataList(result);
+    }
+
     /**
      * grant project
      *
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 2c67861426..f6d8257ec9 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
@@ -280,6 +280,8 @@ public enum Status {
 
     USER_NO_OPERATION_PERM(30001, "user has no operation privilege", "当前用户没有操作权限"),
     USER_NO_OPERATION_PROJECT_PERM(30002, "user {0} is not has project {1} permission", "当前用户[{0}]没有[{1}]项目的操作权限"),
+    USER_NO_WRITE_PROJECT_PERM(30003, "user [{0}] does not have write permission for project [{1}]",
+            "当前用户[{0}]没有[{1}]项目的写权限"),
 
     PROCESS_INSTANCE_NOT_EXIST(50001, "process instance {0} does not exist", "工作流实例[{0}]不存在"),
     PROCESS_INSTANCE_EXIST(50002, "process instance {0} already exists", "工作流实例[{0}]已存在"),
diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/permission/ResourcePermissionCheckServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/permission/ResourcePermissionCheckServiceImpl.java
index 7b3da83bf5..bf3d48e4cf 100644
--- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/permission/ResourcePermissionCheckServiceImpl.java
+++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/permission/ResourcePermissionCheckServiceImpl.java
@@ -242,7 +242,7 @@ public class ResourcePermissionCheckServiceImpl
             }
             List<Resource> ownResourceList = resourceMapper.queryResourceListAuthored(userId, -1);
             relationResources.addAll(ownResourceList);
-            return ownResourceList.stream().map(Resource::getId).collect(toSet());
+            return relationResources.stream().map(Resource::getId).collect(toSet());
         }
 
         @Override
diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ProjectService.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ProjectService.java
index a0d7217766..43b8a04acf 100644
--- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ProjectService.java
+++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ProjectService.java
@@ -81,6 +81,10 @@ public interface ProjectService {
      */
     boolean hasProjectAndPerm(User loginUser, Project project, Result result, String permission);
 
+    boolean hasProjectAndWritePerm(User loginUser, Project project, Result result);
+
+    boolean hasProjectAndWritePerm(User loginUser, Project project, Map<String, Object> result);
+
     /**
      * admin can view all projects
      *
@@ -92,6 +96,19 @@ public interface ProjectService {
      */
     Result queryProjectListPaging(User loginUser, Integer pageSize, Integer pageNo, String searchVal);
 
+    /**
+     * admin can view all projects
+     *
+     * @param userId user id
+     * @param loginUser login user
+     * @param searchVal search value
+     * @param pageSize page size
+     * @param pageNo page number
+     * @return project list which with the login user's authorized level
+     */
+    Result queryProjectWithAuthorizedLevelListPaging(Integer userId, User loginUser, Integer pageSize, Integer pageNo,
+                                                     String searchVal);
+
     /**
      * delete project by code
      *
@@ -131,6 +148,13 @@ public interface ProjectService {
      */
     Result queryAuthorizedProject(User loginUser, Integer userId);
 
+    /**
+     * query all project with authorized level
+     * @param loginUser login user
+     * @return project list
+     */
+    Result queryProjectWithAuthorizedLevel(User loginUser, Integer userId);
+
     /**
      * query authorized user
      *
diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ResourcesService.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ResourcesService.java
index 04f477e36b..c0c7a7d211 100644
--- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ResourcesService.java
+++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ResourcesService.java
@@ -195,7 +195,9 @@ public interface ResourcesService {
     /**
      * updateProcessInstance resource
      *
-     * @param resourceId resource id
+     * @param loginUser login user
+     * @param fullName full name
+     * @param tenantCode tenantCode
      * @param content content
      * @return update result cod
      */
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 d854e9afdd..9abe60c72c 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
@@ -153,6 +153,16 @@ public interface UsersService {
      */
     Map<String, Object> grantProject(User loginUser, int userId, String projectIds);
 
+    /**
+     * grant project with read permission
+     *
+     * @param loginUser login user
+     * @param userId user id
+     * @param projectIds project id array
+     * @return grant result code
+     */
+    Map<String, Object> grantProjectWithReadPerm(User loginUser, int userId, String projectIds);
+
     /**
      * grant project by code
      *
@@ -163,6 +173,15 @@ public interface UsersService {
      */
     Map<String, Object> grantProjectByCode(User loginUser, int userId, long projectCode);
 
+    /**
+     * revoke the project permission for specified user by id
+     * @param loginUser     Login user
+     * @param userId        User id
+     * @param projectIds   project id array
+     * @return
+     */
+    Map<String, Object> revokeProjectById(User loginUser, int userId, String projectIds);
+
     /**
      * revoke the project permission for specified user.
      * @param loginUser     Login user
diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessDefinitionServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessDefinitionServiceImpl.java
index acb05b897e..fc01482236 100644
--- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessDefinitionServiceImpl.java
+++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessDefinitionServiceImpl.java
@@ -261,10 +261,12 @@ public class ProcessDefinitionServiceImpl extends BaseServiceImpl implements Pro
                                                        String otherParamsJson,
                                                        ProcessExecutionTypeEnum executionType) {
         Project project = projectMapper.queryByCode(projectCode);
-        // check user access for project
+
+        // check if user have write perm for project
         Map<String, Object> result =
                 projectService.checkProjectAndAuth(loginUser, project, projectCode, WORKFLOW_CREATE);
-        if (result.get(Constants.STATUS) != Status.SUCCESS) {
+        boolean hasProjectAndWritePerm = projectService.hasProjectAndWritePerm(loginUser, project, result);
+        if (!hasProjectAndWritePerm) {
             return result;
         }
         if (checkDescriptionLength(description)) {
@@ -754,12 +756,14 @@ public class ProcessDefinitionServiceImpl extends BaseServiceImpl implements Pro
                                                        String otherParamsJson,
                                                        ProcessExecutionTypeEnum executionType) {
         Project project = projectMapper.queryByCode(projectCode);
-        // check user access for project
+        // check if user have write perm for project
         Map<String, Object> result =
                 projectService.checkProjectAndAuth(loginUser, project, projectCode, WORKFLOW_UPDATE);
-        if (result.get(Constants.STATUS) != Status.SUCCESS) {
+        boolean hasProjectAndWritePerm = projectService.hasProjectAndWritePerm(loginUser, project, result);
+        if (!hasProjectAndWritePerm) {
             return result;
         }
+
         if (checkDescriptionLength(description)) {
             logger.warn("Parameter description is too long.");
             putMsg(result, Status.DESCRIPTION_TOO_LONG_ERROR);
@@ -2404,12 +2408,14 @@ public class ProcessDefinitionServiceImpl extends BaseServiceImpl implements Pro
     public Map<String, Object> deleteProcessDefinitionVersion(User loginUser, long projectCode, long code,
                                                               int version) {
         Project project = projectMapper.queryByCode(projectCode);
-        // check user access for project
+        // check if user have write perm for project
         Map<String, Object> result =
                 projectService.checkProjectAndAuth(loginUser, project, projectCode, VERSION_DELETE);
-        if (result.get(Constants.STATUS) != Status.SUCCESS) {
+        boolean hasProjectAndWritePerm = projectService.hasProjectAndWritePerm(loginUser, project, result);
+        if (!hasProjectAndWritePerm) {
             return result;
         }
+
         ProcessDefinition processDefinition = processDefinitionMapper.queryByCode(code);
 
         if (processDefinition == null || projectCode != processDefinition.getProjectCode()) {
@@ -2466,10 +2472,11 @@ public class ProcessDefinitionServiceImpl extends BaseServiceImpl implements Pro
                                                             String scheduleJson,
                                                             ProcessExecutionTypeEnum executionType) {
         Project project = projectMapper.queryByCode(projectCode);
-        // check user access for project
+        // check if user have write perm for project
         Map<String, Object> result =
                 projectService.checkProjectAndAuth(loginUser, project, projectCode, WORKFLOW_CREATE);
-        if (result.get(Constants.STATUS) != Status.SUCCESS) {
+        boolean hasProjectAndWritePerm = projectService.hasProjectAndWritePerm(loginUser, project, result);
+        if (!hasProjectAndWritePerm) {
             return result;
         }
         if (checkDescriptionLength(description)) {
@@ -2613,10 +2620,11 @@ public class ProcessDefinitionServiceImpl extends BaseServiceImpl implements Pro
                                                                 String otherParamsJson,
                                                                 ProcessExecutionTypeEnum executionType) {
         Project project = projectMapper.queryByCode(projectCode);
-        // check user access for project
+        // check if user have write perm for project
         Map<String, Object> result =
                 projectService.checkProjectAndAuth(loginUser, project, projectCode, WORKFLOW_UPDATE);
-        if (result.get(Constants.STATUS) != Status.SUCCESS) {
+        boolean hasProjectAndWritePerm = projectService.hasProjectAndWritePerm(loginUser, project, result);
+        if (!hasProjectAndWritePerm) {
             return result;
         }
         if (checkDescriptionLength(description)) {
diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProjectServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProjectServiceImpl.java
index 0ba236dd44..1353a30fed 100644
--- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProjectServiceImpl.java
+++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProjectServiceImpl.java
@@ -20,7 +20,6 @@ package org.apache.dolphinscheduler.api.service.impl;
 import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.PROJECT;
 import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.PROJECT_CREATE;
 import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.PROJECT_DELETE;
-import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.PROJECT_UPDATE;
 
 import org.apache.dolphinscheduler.api.enums.Status;
 import org.apache.dolphinscheduler.api.exceptions.ServiceException;
@@ -256,6 +255,60 @@ public class ProjectServiceImpl extends BaseServiceImpl implements ProjectServic
         return checkResult;
     }
 
+    @Override
+    public boolean hasProjectAndWritePerm(User loginUser, Project project, Result result) {
+        boolean checkResult = false;
+        if (project == null) {
+            logger.error("Project does not exist.");
+            putMsg(result, Status.PROJECT_NOT_FOUND, "");
+        } else {
+            // case 1: user is admin
+            if (loginUser.getUserType() == UserType.ADMIN_USER) {
+                return true;
+            }
+            // case 2: user is project owner
+            if (project.getUserId().equals(loginUser.getId())) {
+                return true;
+            }
+            // case 3: check user permission level
+            ProjectUser projectUser = projectUserMapper.queryProjectRelation(project.getId(), loginUser.getId());
+            if (projectUser == null || projectUser.getPerm() != Constants.DEFAULT_ADMIN_PERMISSION) {
+                putMsg(result, Status.USER_NO_WRITE_PROJECT_PERM, loginUser.getUserName(), project.getCode());
+                checkResult = false;
+            } else {
+                checkResult = true;
+            }
+        }
+        return checkResult;
+    }
+
+    @Override
+    public boolean hasProjectAndWritePerm(User loginUser, Project project, Map<String, Object> result) {
+        boolean checkResult = false;
+        if (project == null) {
+            logger.error("Project does not exist.");
+            putMsg(result, Status.PROJECT_NOT_FOUND, "");
+        } else {
+            // case 1: user is admin
+            if (loginUser.getUserType() == UserType.ADMIN_USER) {
+                return true;
+            }
+            // case 2: user is project owner
+            if (project.getUserId().equals(loginUser.getId())) {
+                return true;
+            }
+            // case 3: check user permission level
+            ProjectUser projectUser = projectUserMapper.queryProjectRelation(project.getId(), loginUser.getId());
+            if (projectUser == null || projectUser.getPerm() != Constants.DEFAULT_ADMIN_PERMISSION) {
+                putMsg(result, Status.USER_NO_WRITE_PROJECT_PERM, loginUser.getUserName(), project.getCode());
+                checkResult = false;
+            } else {
+                checkResult = true;
+            }
+        }
+        return checkResult;
+    }
+
     @Override
     public boolean hasProjectAndPerm(User loginUser, Project project, Result result, String permission) {
         boolean checkResult = false;
@@ -310,6 +363,57 @@ public class ProjectServiceImpl extends BaseServiceImpl implements ProjectServic
         return result;
     }
 
+    /**
+     * admin can view all projects
+     *
+     * @param userId user id
+     * @param loginUser login user
+     * @param searchVal search value
+     * @param pageSize page size
+     * @param pageNo page number
+     * @return project list which with the login user's authorized level
+     */
+    @Override
+    public Result queryProjectWithAuthorizedLevelListPaging(Integer userId, User loginUser, Integer pageSize,
+                                                            Integer pageNo, String searchVal) {
+        Result result = new Result();
+        PageInfo<Project> pageInfo = new PageInfo<>(pageNo, pageSize);
+        Page<Project> page = new Page<>(pageNo, pageSize);
+        Set<Integer> allProjectIds = resourcePermissionCheckService
+                .userOwnedResourceIdsAcquisition(AuthorizationType.PROJECTS, loginUser.getId(), logger);
+        Set<Integer> userProjectIds = resourcePermissionCheckService
+                .userOwnedResourceIdsAcquisition(AuthorizationType.PROJECTS, userId, logger);
+        if (allProjectIds.isEmpty()) {
+            result.setData(pageInfo);
+            putMsg(result, Status.SUCCESS);
+            return result;
+        }
+        IPage<Project> projectIPage =
+                projectMapper.queryProjectListPaging(page, new ArrayList<>(allProjectIds), searchVal);
+
+        List<Project> projectList = projectIPage.getRecords();
+
+        for (Project project : projectList) {
+            if (userProjectIds.contains(project.getId())) {
+                ProjectUser projectUser = projectUserMapper.queryProjectRelation(project.getId(), userId);
+                if (projectUser == null) {
+                    // in this case, the user is the project owner, maybe it's better to set it to ALL_PERMISSION.
+                    project.setPerm(Constants.DEFAULT_ADMIN_PERMISSION);
+                } else {
+                    project.setPerm(projectUser.getPerm());
+                }
+            } else {
+                project.setPerm(0);
+            }
+        }
+
+        pageInfo.setTotal((int) projectIPage.getTotal());
+        pageInfo.setTotalList(projectList);
+        result.setData(pageInfo);
+        putMsg(result, Status.SUCCESS);
+        return result;
+    }
+
     /**
      * delete project by code
      *
@@ -322,6 +426,11 @@ public class ProjectServiceImpl extends BaseServiceImpl implements ProjectServic
         Result result = new Result();
         Project project = projectMapper.queryByCode(projectCode);
 
+        boolean hasProjectAndWritePerm = hasProjectAndWritePerm(loginUser, project, result);
+        if (!hasProjectAndWritePerm) {
+            return result;
+        }
+
         checkProjectAndAuth(result, loginUser, project, project == null ? 0L : project.getCode(), PROJECT_DELETE);
         if (result.getCode() != Status.SUCCESS.getCode()) {
             return result;
@@ -386,8 +495,8 @@ public class ProjectServiceImpl extends BaseServiceImpl implements ProjectServic
         }
 
         Project project = projectMapper.queryByCode(projectCode);
-        boolean hasProjectAndPerm = hasProjectAndPerm(loginUser, project, result, PROJECT_UPDATE);
-        if (!hasProjectAndPerm) {
+        boolean hasProjectAndWritePerm = hasProjectAndWritePerm(loginUser, project, result);
+        if (!hasProjectAndWritePerm) {
             return result;
         }
         Project tempProject = projectMapper.queryByName(projectName);
@@ -417,6 +526,47 @@ public class ProjectServiceImpl extends BaseServiceImpl implements ProjectServic
         return result;
     }
 
+    /**
+     * query all project with authorized level
+     * @param loginUser login user
+     * @return project list
+     */
+    @Override
+    public Result queryProjectWithAuthorizedLevel(User loginUser, Integer userId) {
+        Result result = new Result();
+
+        Set<Integer> projectIds = resourcePermissionCheckService
+                .userOwnedResourceIdsAcquisition(AuthorizationType.PROJECTS, loginUser.getId(), logger);
+        List<Project> projectList = projectMapper.listAuthorizedProjects(
+                loginUser.getUserType().equals(UserType.ADMIN_USER) ? 0 : loginUser.getId(),
+                new ArrayList<>(projectIds));
+
+        List<Project> unauthorizedProjectsList = new ArrayList<>();
+        List<Project> authedProjectList = new ArrayList<>();
+        Set<Project> projectSet;
+        if (projectList != null && !projectList.isEmpty()) {
+            projectSet = new HashSet<>(projectList);
+            authedProjectList = projectMapper.queryAuthedProjectListByUserId(userId);
+            unauthorizedProjectsList = getUnauthorizedProjects(projectSet, authedProjectList);
+        }
+
+        for (int i = 0; i < authedProjectList.size(); i++) {
+            authedProjectList.get(i).setPerm(7);
+        }
+
+        for (int i = 0; i < unauthorizedProjectsList.size(); i++) {
+            unauthorizedProjectsList.get(i).setPerm(0);
+        }
+
+        List<Project> joined = new ArrayList<>();
+        joined.addAll(authedProjectList);
+        joined.addAll(unauthorizedProjectsList);
+
+        result.setData(joined);
+        putMsg(result, Status.SUCCESS);
+        return result;
+    }
+
     /**
      * query unauthorized project
      *
diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/TaskDefinitionServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/TaskDefinitionServiceImpl.java
index c12cbb53f1..1dbc91ad91 100644
--- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/TaskDefinitionServiceImpl.java
+++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/TaskDefinitionServiceImpl.java
@@ -143,10 +143,11 @@ public class TaskDefinitionServiceImpl extends BaseServiceImpl implements TaskDe
                                                     long projectCode,
                                                     String taskDefinitionJson) {
         Project project = projectMapper.queryByCode(projectCode);
-        // check user access for project
+        // check if user have write perm for project
         Map<String, Object> result =
                 projectService.checkProjectAndAuth(loginUser, project, projectCode, TASK_DEFINITION_CREATE);
-        if (result.get(Constants.STATUS) != Status.SUCCESS) {
+        boolean hasProjectAndWritePerm = projectService.hasProjectAndWritePerm(loginUser, project, result);
+        if (!hasProjectAndWritePerm) {
             return result;
         }
 
@@ -285,10 +286,11 @@ public class TaskDefinitionServiceImpl extends BaseServiceImpl implements TaskDe
                                                        String taskDefinitionJsonObj,
                                                        String upstreamCodes) {
         Project project = projectMapper.queryByCode(projectCode);
-        // check user access for project
+        // check if user have write perm for project
         Map<String, Object> result =
                 projectService.checkProjectAndAuth(loginUser, project, projectCode, TASK_DEFINITION_CREATE);
-        if (result.get(Constants.STATUS) != Status.SUCCESS) {
+        boolean hasProjectAndWritePerm = projectService.hasProjectAndWritePerm(loginUser, project, result);
+        if (!hasProjectAndWritePerm) {
             return result;
         }
         ProcessDefinition processDefinition = processDefinitionMapper.queryByCode(processDefinitionCode);
@@ -426,10 +428,16 @@ public class TaskDefinitionServiceImpl extends BaseServiceImpl implements TaskDe
     /**
      * Whether task definition can be deleted or not
      */
-    private void taskCanDeleteValid(User user, TaskDefinition taskDefinition) {
+    private void taskCanDeleteValid(User user, TaskDefinition taskDefinition, User loginUser) {
         // check user access for project
         Project project = projectMapper.queryByCode(taskDefinition.getProjectCode());
         projectService.checkProjectAndAuthThrowException(user, project, TASK_DEFINITION_DELETE);
+        // check if user have write perm for project
+        Map<String, Object> result = new HashMap<>();
+        boolean hasProjectAndWritePerm = projectService.hasProjectAndWritePerm(loginUser, project, result);
+        if (!hasProjectAndWritePerm) {
+            throw new ServiceException(Status.TASK_DEFINE_STATE_ONLINE, taskDefinition.getCode());
+        }
 
         // Whether task relation workflow is online
         if (processService.isTaskOnline(taskDefinition.getCode()) && taskDefinition.getFlag() == Flag.YES) {
@@ -466,7 +474,7 @@ public class TaskDefinitionServiceImpl extends BaseServiceImpl implements TaskDe
             throw new ServiceException(Status.TASK_DEFINE_NOT_EXIST, taskCode);
         }
 
-        this.taskCanDeleteValid(loginUser, taskDefinition);
+        this.taskCanDeleteValid(loginUser, taskDefinition, loginUser);
         int delete = taskDefinitionMapper.deleteByCode(taskCode);
         if (delete <= 0) {
             throw new ServiceException(Status.DELETE_TASK_DEFINE_BY_CODE_MSG_ERROR, taskDefinition.getCode());
@@ -683,11 +691,13 @@ public class TaskDefinitionServiceImpl extends BaseServiceImpl implements TaskDe
     private TaskDefinitionLog updateTask(User loginUser, long projectCode, long taskCode, String taskDefinitionJsonObj,
                                          Map<String, Object> result) {
         Project project = projectMapper.queryByCode(projectCode);
-        // check user access for project
-        result.putAll(projectService.checkProjectAndAuth(loginUser, project, projectCode, TASK_DEFINITION_UPDATE));
-        if (result.get(Constants.STATUS) != Status.SUCCESS) {
+
+        // check if user have write perm for project
+        boolean hasProjectAndWritePerm = projectService.hasProjectAndWritePerm(loginUser, project, result);
+        if (!hasProjectAndWritePerm) {
             return null;
         }
+
         TaskDefinition taskDefinition = taskDefinitionMapper.queryByCode(taskCode);
         if (taskDefinition == null) {
             logger.error("Task definition does not exist, taskDefinitionCode:{}.", taskCode);
@@ -943,10 +953,11 @@ public class TaskDefinitionServiceImpl extends BaseServiceImpl implements TaskDe
     @Override
     public Map<String, Object> deleteByCodeAndVersion(User loginUser, long projectCode, long taskCode, int version) {
         Project project = projectMapper.queryByCode(projectCode);
-        // check user access for project
+        // check if user have write perm for project
         Map<String, Object> result =
                 projectService.checkProjectAndAuth(loginUser, project, projectCode, TASK_DEFINITION_DELETE);
-        if (result.get(Constants.STATUS) != Status.SUCCESS) {
+        boolean hasProjectAndWritePerm = projectService.hasProjectAndWritePerm(loginUser, project, result);
+        if (!hasProjectAndWritePerm) {
             return result;
         }
         TaskDefinition taskDefinition = taskDefinitionMapper.queryByCode(taskCode);
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 3b65634979..f200586fc6 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
@@ -533,6 +533,97 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService {
         }
     }
 
+    /**
+     * revoke the project permission for specified user by id
+     * @param loginUser     Login user
+     * @param userId        User id
+     * @param projectIds   project id array
+     * @return
+     */
+    @Override
+    @Transactional(rollbackFor = RuntimeException.class)
+    public Map<String, Object> revokeProjectById(User loginUser, int userId, String projectIds) {
+        Map<String, Object> result = new HashMap<>();
+        result.put(Constants.STATUS, false);
+
+        if (resourcePermissionCheckService.functionDisabled()) {
+            putMsg(result, Status.FUNCTION_DISABLED);
+            return result;
+        }
+        // 1. only admin can operate
+        if (this.check(result, !this.isAdmin(loginUser), Status.USER_NO_OPERATION_PERM)) {
+            return result;
+        }
+
+        // 2. check if user is existed
+        User user = this.userMapper.selectById(userId);
+        if (user == null) {
+            this.putMsg(result, Status.USER_NOT_EXIST, userId);
+            return result;
+        }
+
+        Arrays.stream(projectIds.split(",")).distinct().forEach(projectId -> {
+            // 3. check if project is existed
+            Project project = this.projectMapper.queryDetailById(Integer.parseInt(projectId));
+            if (project == null) {
+                this.putMsg(result, Status.PROJECT_NOT_FOUND, Integer.parseInt(projectId));
+            } else {
+                // 4. delete the relationship between project and user
+                this.projectUserMapper.deleteProjectRelation(project.getId(), user.getId());
+            }
+        });
+
+        this.putMsg(result, Status.SUCCESS);
+        return result;
+    }
+
+    /**
+     * grant project with read permission
+     *
+     * @param loginUser login user
+     * @param userId user id
+     * @param projectIds project id array
+     * @return grant result code
+     */
+    @Override
+    @Transactional(rollbackFor = RuntimeException.class)
+    public Map<String, Object> grantProjectWithReadPerm(User loginUser, int userId, String projectIds) {
+        Map<String, Object> result = new HashMap<>();
+        result.put(Constants.STATUS, false);
+
+        if (resourcePermissionCheckService.functionDisabled()) {
+            putMsg(result, Status.FUNCTION_DISABLED);
+            return result;
+        }
+        // check exist
+        User tempUser = userMapper.selectById(userId);
+        if (tempUser == null) {
+            putMsg(result, Status.USER_NOT_EXIST, userId);
+            return result;
+        }
+
+        if (check(result, StringUtils.isEmpty(projectIds), Status.SUCCESS)) {
+            return result;
+        }
+        Arrays.stream(projectIds.split(Constants.COMMA)).distinct().forEach(projectId -> {
+            ProjectUser projectUserOld = projectUserMapper.queryProjectRelation(Integer.parseInt(projectId), userId);
+            if (projectUserOld != null) {
+                projectUserMapper.deleteProjectRelation(Integer.parseInt(projectId), userId);
+            }
+            Date now = new Date();
+            ProjectUser projectUser = new ProjectUser();
+            projectUser.setUserId(userId);
+            projectUser.setProjectId(Integer.parseInt(projectId));
+            projectUser.setPerm(Constants.READ_PERMISSION);
+            projectUser.setCreateTime(now);
+            projectUser.setUpdateTime(now);
+            projectUserMapper.insert(projectUser);
+        });
+        putMsg(result, Status.SUCCESS);
+
+        return result;
+    }
+
     /**
      * grant project
      *
@@ -559,13 +650,15 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService {
             return result;
         }
 
-        projectUserMapper.deleteProjectRelation(0, userId);
-
         if (check(result, StringUtils.isEmpty(projectIds), Status.SUCCESS)) {
             logger.warn("Parameter projectIds is empty.");
             return result;
         }
         Arrays.stream(projectIds.split(",")).distinct().forEach(projectId -> {
+            ProjectUser projectUserOld = projectUserMapper.queryProjectRelation(Integer.parseInt(projectId), userId);
+            if (projectUserOld != null) {
+                projectUserMapper.deleteProjectRelation(Integer.parseInt(projectId), userId);
+            }
             Date now = new Date();
             ProjectUser projectUser = new ProjectUser();
             projectUser.setUserId(userId);
diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessDefinitionServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessDefinitionServiceTest.java
index 31502b917e..de8a58f619 100644
--- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessDefinitionServiceTest.java
+++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessDefinitionServiceTest.java
@@ -779,6 +779,7 @@ public class ProcessDefinitionServiceTest extends BaseServiceTestTool {
         Mockito.when(projectMapper.queryByCode(projectCode)).thenReturn(getProject(projectCode));
         Mockito.when(projectService.checkProjectAndAuth(user, project, projectCode, WORKFLOW_UPDATE))
                 .thenReturn(result);
+        Mockito.when(projectService.hasProjectAndWritePerm(user, project, result)).thenReturn(true);
 
         try {
             processDefinitionService.updateProcessDefinition(user, projectCode, "test", 1,
diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProjectServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProjectServiceTest.java
index 5ad77a3e87..f2b3d4d16b 100644
--- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProjectServiceTest.java
+++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProjectServiceTest.java
@@ -22,11 +22,14 @@ import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationCon
 import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.PROJECT_CREATE;
 import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.PROJECT_DELETE;
 import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.PROJECT_UPDATE;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 
 import org.apache.dolphinscheduler.api.enums.Status;
 import org.apache.dolphinscheduler.api.permission.ResourcePermissionCheckService;
 import org.apache.dolphinscheduler.api.service.impl.BaseServiceImpl;
 import org.apache.dolphinscheduler.api.service.impl.ProjectServiceImpl;
+import org.apache.dolphinscheduler.api.utils.PageInfo;
 import org.apache.dolphinscheduler.api.utils.Result;
 import org.apache.dolphinscheduler.common.constants.Constants;
 import org.apache.dolphinscheduler.common.enums.AuthorizationType;
@@ -62,6 +65,9 @@ import org.mockito.quality.Strictness;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+
 /**
  * project service test
  **/
@@ -206,6 +212,66 @@ public class ProjectServiceTest {
         Assertions.assertTrue(checkResult);
     }
 
+    @Test
+    public void testQueryProjectWithAuthorizedLevelListPaging() {
+        IPage<Project> page = new Page<>(1, 10);
+        page.setTotal(1L);
+        page.setRecords(getList());
+
+        User loginUser = getLoginUser();
+        Integer pageSize = 10;
+        Integer pageNo = 1;
+        String searchVal = "testVal";
+        Result result = new Result();
+        Mockito.when(projectMapper.queryProjectListPaging(any(Page.class), Mockito.anyList(), eq(searchVal)))
+                .thenReturn(page);
+
+        Set<Integer> allProjectIds = new HashSet();
+        allProjectIds.add(1);
+        Mockito.when(resourcePermissionCheckService.userOwnedResourceIdsAcquisition(AuthorizationType.PROJECTS,
+                loginUser.getId(), projectLogger)).thenReturn(allProjectIds);
+
+        // SUCCESS
+        result = projectService.queryProjectWithAuthorizedLevelListPaging(loginUser.getId(), loginUser, pageSize,
+                pageNo, searchVal);
+        logger.info(result.toString());
+        PageInfo<Project> pageInfo = (PageInfo<Project>) result.getData();
+        Assertions.assertTrue(CollectionUtils.isNotEmpty(pageInfo.getTotalList()));
+    }
+
+    @Test
+    public void testHasProjectAndWritePerm() {
+
+        // Mockito.when(projectUserMapper.queryProjectRelation(1, 1)).thenReturn(getProjectUser());
+        User loginUser = getLoginUser();
+        Project project = getProject();
+        Map<String, Object> result = new HashMap<>();
+        // not exist user
+        User tempUser = new User();
+        tempUser.setId(Integer.MAX_VALUE);
+        tempUser.setUserType(UserType.GENERAL_USER);
+        Mockito.when(resourcePermissionCheckService.operationPermissionCheck(AuthorizationType.PROJECTS,
+                new Object[]{project.getId()},
+                tempUser.getId(), null, baseServiceLogger)).thenReturn(true);
+        boolean checkResult = projectService.hasProjectAndWritePerm(tempUser, project, result);
+        logger.info(result.toString());
+        Assertions.assertFalse(checkResult);
+
+        // success
+        result = new HashMap<>();
+        project.setUserId(1);
+        loginUser.setUserType(UserType.ADMIN_USER);
+        Mockito.when(resourcePermissionCheckService.operationPermissionCheck(AuthorizationType.PROJECTS,
+                new Object[]{project.getId()},
+                loginUser.getId(), null, baseServiceLogger)).thenReturn(true);
+        Mockito.when(resourcePermissionCheckService.resourcePermissionCheck(AuthorizationType.PROJECTS,
+                new Object[]{project.getId()},
+                0, baseServiceLogger)).thenReturn(true);
+        checkResult = projectService.hasProjectAndWritePerm(loginUser, project, result);
+        logger.info(result.toString());
+        Assertions.assertTrue(checkResult);
+    }
+
     @Test
     public void testDeleteProject() {
         User loginUser = getLoginUser();
@@ -213,10 +279,10 @@ public class ProjectServiceTest {
         Mockito.when(resourcePermissionCheckService.operationPermissionCheck(AuthorizationType.PROJECTS,
                 new Object[]{1}, loginUser.getId(),
                 PROJECT_DELETE, baseServiceLogger)).thenReturn(true);
-        // PROJECT_NOT_FOUNT
+        // PROJECT_NOT_FOUND
         Result result = projectService.deleteProject(loginUser, 11L);
         logger.info(result.toString());
-        Assertions.assertTrue(Status.PROJECT_NOT_EXIST.getCode() == result.getCode());
+        Assertions.assertTrue(Status.PROJECT_NOT_FOUND.getCode() == result.getCode());
         loginUser.setId(2);
         // USER_NO_OPERATION_PROJECT_PERM
         Mockito.when(resourcePermissionCheckService.resourcePermissionCheck(AuthorizationType.PROJECTS, new Object[]{1},
@@ -224,7 +290,7 @@ public class ProjectServiceTest {
                 baseServiceLogger)).thenReturn(true);
         result = projectService.deleteProject(loginUser, 1L);
         logger.info(result.toString());
-        Assertions.assertTrue(Status.USER_NO_OPERATION_PROJECT_PERM.getCode() == result.getCode());
+        Assertions.assertTrue(Status.USER_NO_WRITE_PROJECT_PERM.getCode() == result.getCode());
 
         // DELETE_PROJECT_ERROR_DEFINES_NOT_NULL
         Mockito.when(processDefinitionMapper.queryAllDefinitionList(1L)).thenReturn(getProcessDefinitions());
@@ -418,6 +484,40 @@ public class ProjectServiceTest {
 
     }
 
+    @Test
+    public void testQueryProjectWithAuthorizedLevel() {
+        Set<Integer> set = new HashSet();
+        set.add(1);
+        // test admin user
+        User loginUser = new User();
+        loginUser.setUserType(UserType.ADMIN_USER);
+        loginUser.setId(1);
+        List<Integer> list = new ArrayList<>(1);
+        list.add(1);
+        Mockito.when(resourcePermissionCheckService.userOwnedResourceIdsAcquisition(AuthorizationType.PROJECTS,
+                loginUser.getId(), projectLogger)).thenReturn(set);
+        Mockito.when(projectMapper.listAuthorizedProjects(
+                loginUser.getUserType().equals(UserType.ADMIN_USER) ? 0 : loginUser.getId(), list))
+                .thenReturn(getList());
+        Result result = projectService.queryProjectWithAuthorizedLevel(loginUser, 2);
+        logger.info(result.toString());
+        List<Project> projects = (List<Project>) result.getData();
+        Assertions.assertTrue(CollectionUtils.isNotEmpty(projects));
+
+        // test non-admin user
+        loginUser.setId(2);
+        loginUser.setUserType(UserType.GENERAL_USER);
+        Mockito.when(resourcePermissionCheckService.userOwnedResourceIdsAcquisition(AuthorizationType.PROJECTS,
+                loginUser.getId(), projectLogger)).thenReturn(set);
+        Mockito.when(projectMapper.listAuthorizedProjects(
+                loginUser.getUserType().equals(UserType.ADMIN_USER) ? 0 : loginUser.getId(), list))
+                .thenReturn(getList());
+        result = projectService.queryProjectWithAuthorizedLevel(loginUser, 3);
+        logger.info(result.toString());
+        projects = (List<Project>) result.getData();
+        Assertions.assertTrue(CollectionUtils.isNotEmpty(projects));
+    }
+
     @Test
     public void testQueryUnauthorizedProject() {
         Set<Integer> set = new HashSet();
diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/TaskDefinitionServiceImplTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/TaskDefinitionServiceImplTest.java
index 798367c422..0c271ace6f 100644
--- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/TaskDefinitionServiceImplTest.java
+++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/TaskDefinitionServiceImplTest.java
@@ -141,10 +141,6 @@ public class TaskDefinitionServiceImplTest {
                         + "\\\\\\\"failedNode\\\\\\\":[\\\\\\\"\\\\\\\"]}\\\",\\\"dependence\\\":{}}\",\"flag\":0,\"taskPriority\":0,"
                         + "\"workerGroup\":\"default\",\"failRetryTimes\":0,\"failRetryInterval\":0,\"timeoutFlag\":0,"
                         + "\"timeoutNotifyStrategy\":0,\"timeout\":0,\"delayTime\":0,\"resourceIds\":\"\"}]";
-        List<TaskDefinitionLog> taskDefinitions = JSONUtils.toList(createTaskDefinitionJson, TaskDefinitionLog.class);
-        Mockito.when(processService.saveTaskDefine(user, PROJECT_CODE, taskDefinitions, Boolean.TRUE))
-                .thenReturn(1);
-        Mockito.when(taskPluginManager.checkTaskParameters(Mockito.any())).thenReturn(true);
         Map<String, Object> relation = taskDefinitionService
                 .createTaskDefinition(user, PROJECT_CODE, createTaskDefinitionJson);
         Assertions.assertEquals(Status.SUCCESS, relation.get(Constants.STATUS));
@@ -166,8 +162,7 @@ public class TaskDefinitionServiceImplTest {
 
         Map<String, Object> result = new HashMap<>();
         putMsg(result, Status.SUCCESS, PROJECT_CODE);
-        Mockito.when(projectService.checkProjectAndAuth(user, project, PROJECT_CODE, TASK_DEFINITION_UPDATE))
-                .thenReturn(result);
+        Mockito.when(projectService.hasProjectAndWritePerm(user, project, new HashMap<>())).thenReturn(true);
 
         Mockito.when(processService.isTaskOnline(TASK_CODE)).thenReturn(Boolean.FALSE);
         Mockito.when(taskDefinitionMapper.queryByCode(TASK_CODE)).thenReturn(new TaskDefinition());
@@ -212,6 +207,7 @@ public class TaskDefinitionServiceImplTest {
         // error delete single task definition object
         Mockito.when(taskDefinitionMapper.queryByCode(TASK_CODE)).thenReturn(getTaskDefinition());
         Mockito.when(taskDefinitionMapper.deleteByCode(TASK_CODE)).thenReturn(0);
+        Mockito.when(projectService.hasProjectAndWritePerm(user, project, new HashMap<>())).thenReturn(true);
         exception = Assertions.assertThrows(ServiceException.class,
                 () -> taskDefinitionService.deleteTaskDefinitionByCode(user, TASK_CODE));
         Assertions.assertEquals(Status.DELETE_TASK_DEFINE_BY_CODE_MSG_ERROR.getCode(),
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 aae27f88b2..8a9c6506be 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
@@ -369,6 +369,27 @@ public class UsersServiceTest {
         Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
     }
 
+    @Test
+    public void testGrantProjectWithReadPerm() {
+        String projectIds = "100000,120000";
+        User loginUser = new User();
+        int userId = 3;
+
+        // user not exist
+        loginUser.setId(1);
+        loginUser.setUserType(UserType.ADMIN_USER);
+        when(userMapper.selectById(userId)).thenReturn(null);
+        Map<String, Object> result = usersService.grantProjectWithReadPerm(loginUser, userId, projectIds);
+        logger.info(result.toString());
+        Assertions.assertEquals(Status.USER_NOT_EXIST, result.get(Constants.STATUS));
+
+        // SUCCESS
+        when(userMapper.selectById(userId)).thenReturn(getUser());
+        result = usersService.grantProjectWithReadPerm(loginUser, userId, projectIds);
+        logger.info(result.toString());
+        Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
+    }
+
     @Test
     public void testGrantProjectByCode() {
         // Mock Project, User
@@ -440,6 +461,31 @@ public class UsersServiceTest {
         Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
     }
 
+    @Test
+    public void testRevokeProjectById() {
+        Mockito.when(this.userMapper.selectById(1)).thenReturn(this.getUser());
+
+        String projectId = "100000";
+
+        // user no permission
+        User loginUser = new User();
+        Map<String, Object> result = this.usersService.revokeProjectById(loginUser, 1, projectId);
+        logger.info(result.toString());
+        Assertions.assertEquals(Status.USER_NO_OPERATION_PERM, result.get(Constants.STATUS));
+
+        // user not exist
+        loginUser.setUserType(UserType.ADMIN_USER);
+        result = this.usersService.revokeProjectById(loginUser, 2, projectId);
+        logger.info(result.toString());
+        Assertions.assertEquals(Status.USER_NOT_EXIST, result.get(Constants.STATUS));
+
+        // success
+        Mockito.when(this.projectMapper.queryByCode(Mockito.anyLong())).thenReturn(new Project());
+        result = this.usersService.revokeProjectById(loginUser, 1, projectId);
+        logger.info(result.toString());
+        Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
+    }
+
     @Test
     public void testGrantResources() {
         String resourceIds = "100000,120000";
diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/ProjectUserMapper.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/ProjectUserMapper.java
index b5496c438d..38c4a2b4c9 100644
--- a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/ProjectUserMapper.java
+++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/ProjectUserMapper.java
@@ -29,7 +29,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 public interface ProjectUserMapper extends BaseMapper<ProjectUser> {
 
     /**
-     * delte prject user relation
+     * delete project user relation
      *
      * @param projectId projectId
      * @param userId userId
diff --git a/dolphinscheduler-ui/src/common/column-width-config.ts b/dolphinscheduler-ui/src/common/column-width-config.ts
index 7df8dbc321..d292a9a84f 100644
--- a/dolphinscheduler-ui/src/common/column-width-config.ts
+++ b/dolphinscheduler-ui/src/common/column-width-config.ts
@@ -95,6 +95,9 @@ export const COLUMN_WIDTH_CONFIG = {
   tag: {
     width: 160
   },
+  checkbox:{
+    width: 20
+  },
   copy: {
     width: 50
   }
diff --git a/dolphinscheduler-ui/src/locales/en_US/project.ts b/dolphinscheduler-ui/src/locales/en_US/project.ts
index 115cb70967..0aa20551f7 100644
--- a/dolphinscheduler-ui/src/locales/en_US/project.ts
+++ b/dolphinscheduler-ui/src/locales/en_US/project.ts
@@ -36,7 +36,11 @@ export default {
     delete: 'Delete',
     confirm: 'Confirm',
     cancel: 'Cancel',
-    delete_confirm: 'Delete?'
+    delete_confirm: 'Delete?',
+    authorize_level:'Authorize Level',
+    no_permission: 'No Permission',
+    read_permission: 'Read Permission',
+    all_permission: 'All Permission',
   },
   workflow: {
     on_line: 'Online',
diff --git a/dolphinscheduler-ui/src/locales/en_US/security.ts b/dolphinscheduler-ui/src/locales/en_US/security.ts
index 2dbb66fe84..4318cdb0c8 100644
--- a/dolphinscheduler-ui/src/locales/en_US/security.ts
+++ b/dolphinscheduler-ui/src/locales/en_US/security.ts
@@ -150,6 +150,9 @@ export default {
     datasource: 'Datasource',
     udf: 'UDF Function',
     namespace: 'Namespace',
+    revoke_auth: 'Revoke',
+    grant_read: 'Grant Read',
+    grant_all:'Grant All',
     authorize_project: 'Project Authorize',
     authorize_resource: 'Resource Authorize',
     authorize_namespace: 'Namespace Authorize',
diff --git a/dolphinscheduler-ui/src/locales/zh_CN/project.ts b/dolphinscheduler-ui/src/locales/zh_CN/project.ts
index 31c5c64617..fe9f14b279 100644
--- a/dolphinscheduler-ui/src/locales/zh_CN/project.ts
+++ b/dolphinscheduler-ui/src/locales/zh_CN/project.ts
@@ -36,7 +36,11 @@ export default {
     delete: '删除',
     confirm: '确定',
     cancel: '取消',
-    delete_confirm: '确定删除吗?'
+    delete_confirm: '确定删除吗?',
+    authorize_level:'权限等级',
+    no_permission: '无权限',
+    read_permission: '读权限',
+    all_permission: '所有权限',
   },
   workflow: {
     on_line: '线上',
diff --git a/dolphinscheduler-ui/src/locales/zh_CN/security.ts b/dolphinscheduler-ui/src/locales/zh_CN/security.ts
index 9135ba78e4..995293ec68 100644
--- a/dolphinscheduler-ui/src/locales/zh_CN/security.ts
+++ b/dolphinscheduler-ui/src/locales/zh_CN/security.ts
@@ -148,6 +148,9 @@ export default {
     datasource: '数据源',
     udf: 'UDF函数',
     namespace: '命名空间',
+    revoke_auth: '撤销权限',
+    grant_read: '授予读权限',
+    grant_all:'授予所有权限',
     authorize_project: '项目授权',
     authorize_resource: '资源授权',
     authorize_namespace: '命名空间授权',
diff --git a/dolphinscheduler-ui/src/service/modules/projects/index.ts b/dolphinscheduler-ui/src/service/modules/projects/index.ts
index 69e097c7b8..e426d47f5e 100644
--- a/dolphinscheduler-ui/src/service/modules/projects/index.ts
+++ b/dolphinscheduler-ui/src/service/modules/projects/index.ts
@@ -16,7 +16,7 @@
  */
 
 import { axios } from '@/service/service'
-import { ListReq, ProjectsReq, UserIdReq, UpdateProjectsReq } from './types'
+import { ListReq, ListIdReq, ProjectsReq, UserIdReq, UpdateProjectsReq } from './types'
 
 export function queryProjectListPaging(params: ListReq): any {
   return axios({
@@ -26,6 +26,14 @@ export function queryProjectListPaging(params: ListReq): any {
   })
 }
 
+export function queryProjectWithAuthorizedLevelListPaging(params: ListIdReq): any {
+  return axios({
+    url: '/projects/project-with-authorized-level-list-paging',
+    method: 'get',
+    params
+  })
+}
+
 export function createProject(data: ProjectsReq): any {
   return axios({
     url: '/projects',
@@ -64,6 +72,14 @@ export function queryUnauthorizedProject(params: UserIdReq): any {
   })
 }
 
+export function queryProjectWithAuthorizedLevel(params: UserIdReq): any {
+  return axios({
+    url: '/projects/project-with-authorized-level',
+    method: 'get',
+    params
+  })
+}
+
 export function queryProjectByCode(code: number): any {
   return axios({
     url: `/projects/${code}`,
diff --git a/dolphinscheduler-ui/src/service/modules/projects/types.ts b/dolphinscheduler-ui/src/service/modules/projects/types.ts
index 6b02d52ba0..3d0d824c8f 100644
--- a/dolphinscheduler-ui/src/service/modules/projects/types.ts
+++ b/dolphinscheduler-ui/src/service/modules/projects/types.ts
@@ -21,6 +21,13 @@ interface ListReq {
   searchVal?: string
 }
 
+interface ListIdReq {
+  userId?: number
+  pageNo: number
+  pageSize: number
+  searchVal?: string
+}
+
 interface ProjectsReq {
   description?: string
   projectName: string
@@ -59,6 +66,7 @@ interface ProjectRes {
 
 export {
   ListReq,
+  ListIdReq,
   ProjectsReq,
   UserIdReq,
   UpdateProjectsReq,
diff --git a/dolphinscheduler-ui/src/service/modules/users/index.ts b/dolphinscheduler-ui/src/service/modules/users/index.ts
index 9a1313cbfb..ce3806e4af 100644
--- a/dolphinscheduler-ui/src/service/modules/users/index.ts
+++ b/dolphinscheduler-ui/src/service/modules/users/index.ts
@@ -97,6 +97,14 @@ export function grantResource(data: GrantResourceReq) {
   })
 }
 
+export function revokeProjectById(data: GrantProject) {
+  return axios({
+    url: '/users/revoke-project-by-id',
+    method: 'post',
+    data
+  })
+}
+
 export function grantProject(data: GrantProject) {
   return axios({
     url: '/users/grant-project',
@@ -105,6 +113,14 @@ export function grantProject(data: GrantProject) {
   })
 }
 
+export function grantProjectWithReadPerm(data: GrantProject) {
+  return axios({
+    url: 'users/grant-project-with-read-perm',
+    method: 'post',
+    data
+  })
+}
+
 export function grantProjectByCode(data: ProjectCodeReq & UserIdReq): any {
   return axios({
     url: '/users/grant-project-by-code',
diff --git a/dolphinscheduler-ui/src/views/security/user-manage/components/authorize-modal.tsx b/dolphinscheduler-ui/src/views/security/user-manage/components/authorize-modal.tsx
index bce9c567c6..e0855d9d5b 100644
--- a/dolphinscheduler-ui/src/views/security/user-manage/components/authorize-modal.tsx
+++ b/dolphinscheduler-ui/src/views/security/user-manage/components/authorize-modal.tsx
@@ -15,19 +15,26 @@
  * limitations under the License.
  */
 
-import { defineComponent, PropType, toRefs, watch } from 'vue'
+import { defineComponent, PropType, toRefs, watch} from 'vue'
 import { useI18n } from 'vue-i18n'
 import {
+  NInput,
+  NButton,
+  NIcon,
   NTransfer,
   NSpace,
   NRadioGroup,
   NRadioButton,
-  NTreeSelect
+  NTreeSelect,
+  NDataTable,
+  NPagination
 } from 'naive-ui'
 import { useAuthorize } from './use-authorize'
 import Modal from '@/components/modal'
 import styles from '../index.module.scss'
 import type { TAuthType } from '../types'
+import { useColumns } from './use-columns'
+import { SearchOutlined } from '@vicons/antd'
 
 const props = {
   show: {
@@ -43,14 +50,13 @@ const props = {
     default: 'auth_project'
   }
 }
-
 export const AuthorizeModal = defineComponent({
   name: 'authorize-project-modal',
   props,
   emits: ['cancel'],
   setup(props, ctx) {
     const { t } = useI18n()
-    const { state, onInit, onSave } = useAuthorize()
+    const { state, onInit, onSave, getProjects, revokeProjectByIdRequest, grantProjectRequest, grantProjectWithReadPermRequest, requestData, handleChangePageSize } = useAuthorize()
     const onCancel = () => {
       ctx.emit('cancel')
     }
@@ -59,6 +65,20 @@ export const AuthorizeModal = defineComponent({
       if (result) onCancel()
     }
 
+    const onRevokeProject = () => {
+      revokeProjectByIdRequest(props.userId, state.projectIds)
+    }
+    const onGrantReadPerm = () => {
+      grantProjectWithReadPermRequest(props.userId, state.projectIds)
+    }
+    const onGrantAllPerm = () => {
+      grantProjectRequest(props.userId, state.projectIds)
+    }
+
+    const { columnsRef } = useColumns()
+    const handleCheck = (rowKeys: Array<number>) => {
+      state.projectIds = rowKeys.join()
+    }
     watch(
       () => props.show,
       () => {
@@ -70,14 +90,23 @@ export const AuthorizeModal = defineComponent({
 
     return {
       t,
+      columnsRef,
+      rowKey: (row: any) => row.id,
       ...toRefs(state),
       onCancel,
-      onConfirm
+      onConfirm,
+      getProjects,
+      handleCheck,
+      requestData, 
+      handleChangePageSize,
+      onRevokeProject,
+      onGrantReadPerm,
+      onGrantAllPerm
     }
   },
-  render(props: { type: TAuthType }) {
+  render(props: { type: TAuthType, userId: number }) {
     const { t } = this
-    const { type } = props
+    const { type, userId } = props
     return (
       <Modal
         show={this.show}
@@ -89,13 +118,54 @@ export const AuthorizeModal = defineComponent({
         cancelClassName='btn-cancel'
       >
         {type === 'authorize_project' && (
-          <NTransfer
+
+          <NSpace vertical>
+            <NSpace>
+              <NButton size='small' type='primary' onClick={this.onRevokeProject}>
+                {t('security.user.revoke_auth')}
+              </NButton>
+              <NButton size='small' type='primary' onClick={this.onGrantReadPerm}>
+              {t('security.user.grant_read')}
+              </NButton>
+              <NButton size='small' type='primary' onClick={this.onGrantAllPerm}>
+              {t('security.user.grant_all')}
+              </NButton>
+              <NInput
+                size='small'
+                placeholder={t('project.list.project_tips')}
+                clearable
+                v-model:value={this.searchVal}
+              />
+              {/* <NButton size='small' type='primary' onClick={this.handleSearch}> */}
+              <NButton size='small' type='primary' onClick={() => this.getProjects(userId)}>
+                <NIcon>
+                  <SearchOutlined />
+                </NIcon>
+              </NButton>
+            </NSpace>
+          <NDataTable
             virtualScroll
-            options={this.unauthorizedProjects}
-            filterable
-            v-model={[this.authorizedProjects, 'value']}
-            class={styles.transfer}
+            row-class-name='items'
+            columns={this.columnsRef.columns}
+            data={this.projectWithAuthorizedLevel}
+            loading={this.loading}
+            max-height="250"
+            row-key={this.rowKey}
+            on-update:checked-row-keys={this.handleCheck}
           />
+          <div class={styles.pagination}>
+            <NPagination
+              v-model:page={this.pagination.page}
+              v-model:page-size={this.pagination.pageSize}
+              page-count={this.pagination.totalPage}
+              show-size-picker
+              page-sizes={[5, 10]}
+              show-quick-jumper
+              onUpdatePage={this.requestData}
+              onUpdatePageSize={this.handleChangePageSize}
+            />
+          </div>
+          </NSpace>
         )}
         {type === 'authorize_datasource' && (
           <NTransfer
diff --git a/dolphinscheduler-ui/src/views/security/user-manage/components/use-authorize.ts b/dolphinscheduler-ui/src/views/security/user-manage/components/use-authorize.ts
index 4d7d26b41f..e1cfbd9438 100644
--- a/dolphinscheduler-ui/src/views/security/user-manage/components/use-authorize.ts
+++ b/dolphinscheduler-ui/src/views/security/user-manage/components/use-authorize.ts
@@ -16,8 +16,7 @@
  */
 import { reactive } from 'vue'
 import {
-  queryAuthorizedProject,
-  queryUnauthorizedProject
+  queryProjectWithAuthorizedLevelListPaging
 } from '@/service/modules/projects'
 import {
   authedDatasource,
@@ -36,17 +35,22 @@ import {
 import {
   grantProject,
   grantResource,
+  grantProjectWithReadPerm,
   grantDataSource,
   grantUDFFunc,
-  grantNamespaceFunc
+  grantNamespaceFunc,
+  revokeProjectById,
 } from '@/service/modules/users'
 import utils from '@/utils'
-import type { TAuthType, IResourceOption, IOption } from '../types'
+import type { TAuthType, IResourceOption, IOption, IRecord } from '../types'
 
 export function useAuthorize() {
   const state = reactive({
     saving: false,
     loading: false,
+    projectIds: '',
+    currentRecord: {} as IRecord | null,
+    projectWithAuthorizedLevel: [],
     authorizedProjects: [] as number[],
     unauthorizedProjects: [] as IOption[],
     authorizedDatasources: [] as number[],
@@ -59,26 +63,69 @@ export function useAuthorize() {
     fileResources: [] as IResourceOption[],
     udfResources: [] as IResourceOption[],
     authorizedFileResources: [] as number[],
-    authorizedUdfResources: [] as number[]
+    authorizedUdfResources: [] as number[],
+    pagination: {
+      pageSize: 5,
+      page: 1,
+      totalPage: 0
+    },
+    searchVal: '',
+    userId: 0
   })
 
   const getProjects = async (userId: number) => {
     if (state.loading) return
     state.loading = true
-    const projects = await Promise.all([
-      queryAuthorizedProject({ userId }),
-      queryUnauthorizedProject({ userId })
-    ])
+    if (userId) {
+      state.userId = userId
+    }
+    
+    const projectsList = await queryProjectWithAuthorizedLevelListPaging({
+      userId,
+      searchVal: state.searchVal,
+      pageSize: state.pagination.pageSize,
+      pageNo: state.pagination.page
+    })
     state.loading = false
-    state.authorizedProjects = projects[0].map(
-      (item: { name: string; id: number }) => item.id
-    )
-    state.unauthorizedProjects = [...projects[0], ...projects[1]].map(
-      (item: { name: string; id: number }) => ({
-        label: item.name,
-        value: item.id
-      })
-    )
+    if (!projectsList) throw Error()
+    state.pagination.totalPage = projectsList.totalPage
+    state.projectWithAuthorizedLevel = projectsList.totalList
+    return state.projectWithAuthorizedLevel
+  }
+
+  const requestData = async (page: number) => {
+    state.pagination.page = page
+    await getProjects(state.userId)
+  }
+
+  const handleChangePageSize = async (pageSize: number) => {
+    state.pagination.page = 1
+    state.pagination.pageSize = pageSize
+    await getProjects(state.userId)
+  }
+
+  const revokeProjectByIdRequest = async (userId: number, projectIds: string) => {
+    await revokeProjectById({
+      userId,
+      projectIds: projectIds
+    })
+    await getProjects(userId)
+  }
+
+  const grantProjectRequest = async (userId: number, projectIds: string) => {
+    await grantProject({
+      userId,
+      projectIds: projectIds
+    })
+    await getProjects(userId)
+  }
+
+  const grantProjectWithReadPermRequest = async (userId: number, projectIds: string) => {
+    await grantProjectWithReadPerm({
+      userId,
+      projectIds: projectIds
+    })
+    await getProjects(userId)
   }
 
   const getDatasources = async (userId: number) => {
@@ -216,12 +263,6 @@ export function useAuthorize() {
   const onSave = async (type: TAuthType, userId: number) => {
     if (state.saving) return false
     state.saving = true
-    if (type === 'authorize_project') {
-      await grantProject({
-        userId,
-        projectIds: state.authorizedProjects.join(',')
-      })
-    }
     if (type === 'authorize_datasource') {
       await grantDataSource({
         userId,
@@ -281,5 +322,5 @@ export function useAuthorize() {
     return true
   }
 
-  return { state, onInit, onSave }
+  return { state, onInit, onSave, getProjects, revokeProjectByIdRequest, grantProjectRequest, grantProjectWithReadPermRequest, requestData, handleChangePageSize }
 }
diff --git a/dolphinscheduler-ui/src/views/security/user-manage/components/use-columns.ts b/dolphinscheduler-ui/src/views/security/user-manage/components/use-columns.ts
new file mode 100644
index 0000000000..9e7a609136
--- /dev/null
+++ b/dolphinscheduler-ui/src/views/security/user-manage/components/use-columns.ts
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+import { ref, watch, onMounted } from 'vue'
+import { useI18n } from 'vue-i18n'
+import {
+  COLUMN_WIDTH_CONFIG,
+  calculateTableWidth,
+  DefaultTableWidth
+} from '@/common/column-width-config'
+import type { TableColumns } from '../types'
+
+// const { t } = useI18n()
+const PERM_LIST = [
+  {
+    label: 'project.list.no_permission',
+    value: 0
+  },
+  {
+    label: 'project.list.read_permission',
+    value: 2
+  },
+  {
+    label: 'project.list.all_permission',
+    value: 7
+  }
+]
+  
+
+export function useColumns() {
+  const { t } = useI18n()
+
+  const columnsRef = ref({
+    columns: [] as TableColumns,
+    tableWidth: DefaultTableWidth
+  })
+
+  const createColumns = () => {
+    const columns: any = [
+      {
+        type: 'selection',
+        key: 'selection',
+        ...COLUMN_WIDTH_CONFIG['checkbox']
+      },
+      {
+        title: t('project.list.project_name'),
+        key: 'name',
+        ...COLUMN_WIDTH_CONFIG['size']
+      },
+      {
+        title: t('project.list.authorize_level'),
+        key: 'perm',
+        render: (record: any):any => {
+          return PERM_LIST.filter(item => item.value == record.perm).map(item => t(item.label))
+        },
+        ...COLUMN_WIDTH_CONFIG['index']
+      }
+    ]
+    columnsRef.value = {
+      columns,
+      tableWidth: calculateTableWidth(columns)
+    }
+  }
+
+  onMounted(() => {
+    createColumns()
+  })
+
+  watch(useI18n().locale, () => {
+    createColumns()
+  })
+
+  return {
+    columnsRef,
+    createColumns
+  }
+}
diff --git a/dolphinscheduler-ui/src/views/security/user-manage/index.module.scss b/dolphinscheduler-ui/src/views/security/user-manage/index.module.scss
index 68d62fdd11..4bdd2c4c60 100644
--- a/dolphinscheduler-ui/src/views/security/user-manage/index.module.scss
+++ b/dolphinscheduler-ui/src/views/security/user-manage/index.module.scss
@@ -18,3 +18,8 @@
 .transfer {
   width: 100%;
 }
+.pagination {
+  margin-top: 10px;
+  display: flex;
+  justify-content: center;
+}