You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dolphinscheduler.apache.org by le...@apache.org on 2021/12/13 09:56:11 UTC

[dolphinscheduler] branch 2.0.2-prepare updated: [Cherry-pick-2.0.2][Feature][Dolphinscheduler-api] cherry-pick from dev to 2.0.2 (#7373)

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

leonbao pushed a commit to branch 2.0.2-prepare
in repository https://gitbox.apache.org/repos/asf/dolphinscheduler.git


The following commit(s) were added to refs/heads/2.0.2-prepare by this push:
     new 65a6b58  [Cherry-pick-2.0.2][Feature][Dolphinscheduler-api] cherry-pick from dev to 2.0.2 (#7373)
65a6b58 is described below

commit 65a6b580ab27d13a7755a25a8b895dce77a451fb
Author: ouyangyewei <ou...@foxmail.com>
AuthorDate: Mon Dec 13 17:56:06 2021 +0800

    [Cherry-pick-2.0.2][Feature][Dolphinscheduler-api] cherry-pick from dev to 2.0.2 (#7373)
    
    * [Feature-7110][dolphinscheduler-api] support grant project by code
    
    * [Feature-7180][dolphinscheduler-api] Revoke project permission for specified user
    
    * [Feature-7191][dolphinscheduler-api] support query authorized user list by project code
    
    * [Feature-7301][dolphinscheduler-api] Support query access token for specified user
    
    Co-authored-by: ouyangyewei <ye...@alibaba-inc.com>
---
 .../api/controller/AccessTokenController.java      | 22 ++++++
 .../api/controller/ProjectController.java          | 22 ++++++
 .../api/controller/UsersController.java            | 49 +++++++++++++
 .../apache/dolphinscheduler/api/enums/Status.java  |  4 +-
 .../api/service/AccessTokenService.java            |  9 +++
 .../api/service/ProjectService.java                |  9 +++
 .../dolphinscheduler/api/service/UsersService.java | 19 +++++
 .../api/service/impl/AccessTokenServiceImpl.java   | 25 +++++++
 .../api/service/impl/ProjectServiceImpl.java       | 25 +++++++
 .../api/service/impl/UsersServiceImpl.java         | 84 ++++++++++++++++++++++
 .../src/main/resources/i18n/messages.properties    |  5 ++
 .../main/resources/i18n/messages_en_US.properties  |  5 ++
 .../main/resources/i18n/messages_zh_CN.properties  |  5 ++
 .../api/controller/AccessTokenControllerTest.java  | 13 ++++
 .../api/controller/ProjectControllerTest.java      | 10 +++
 .../api/controller/UsersControllerTest.java        | 37 ++++++++++
 .../api/service/AccessTokenServiceTest.java        | 21 ++++++
 .../api/service/ProjectServiceTest.java            | 54 ++++++++++++++
 .../api/service/UsersServiceTest.java              | 84 ++++++++++++++++++++++
 .../dao/mapper/AccessTokenMapper.java              | 10 +++
 .../dolphinscheduler/dao/mapper/UserMapper.java    |  7 ++
 .../dao/mapper/AccessTokenMapper.xml               |  7 ++
 .../dolphinscheduler/dao/mapper/UserMapper.xml     |  8 +++
 23 files changed, 533 insertions(+), 1 deletion(-)

diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AccessTokenController.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AccessTokenController.java
index 01bb22d..a841bd6 100644
--- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AccessTokenController.java
+++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AccessTokenController.java
@@ -17,6 +17,7 @@
 
 package org.apache.dolphinscheduler.api.controller;
 
+import static org.apache.dolphinscheduler.api.enums.Status.QUERY_ACCESSTOKEN_BY_USER_ERROR;
 import static org.apache.dolphinscheduler.api.enums.Status.CREATE_ACCESS_TOKEN_ERROR;
 import static org.apache.dolphinscheduler.api.enums.Status.DELETE_ACCESS_TOKEN_ERROR;
 import static org.apache.dolphinscheduler.api.enums.Status.GENERATE_TOKEN_ERROR;
@@ -141,6 +142,27 @@ public class AccessTokenController extends BaseController {
     }
 
     /**
+     * query access token for specified user
+     *
+     * @param loginUser login user
+     * @param userId user id
+     * @return token list for specified user
+     */
+    @ApiOperation(value = "queryAccessTokenByUser", notes = "QUERY_ACCESS_TOKEN_BY_USER_NOTES")
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "userId", value = "USER_ID", dataType = "Int")
+    })
+    @GetMapping(value = "/user/{userId}")
+    @ResponseStatus(HttpStatus.OK)
+    @ApiException(QUERY_ACCESSTOKEN_BY_USER_ERROR)
+    @AccessLogAnnotation(ignoreRequestArgs = "loginUser")
+    public Result queryAccessTokenByUser(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+            @PathVariable("userId") Integer userId) {
+        Map<String, Object> result = this.accessTokenService.queryAccessTokenByUser(loginUser, userId);
+        return this.returnDataList(result);
+    }
+
+    /**
      * delete access token by id
      *
      * @param loginUser login user
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 1dfb7e5..22e5822 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
@@ -17,6 +17,7 @@
 
 package org.apache.dolphinscheduler.api.controller;
 
+import static org.apache.dolphinscheduler.api.enums.Status.QUERY_AUTHORIZED_USER;
 import static org.apache.dolphinscheduler.api.enums.Status.CREATE_PROJECT_ERROR;
 import static org.apache.dolphinscheduler.api.enums.Status.DELETE_PROJECT_ERROR;
 import static org.apache.dolphinscheduler.api.enums.Status.LOGIN_USER_QUERY_PROJECT_LIST_PAGING_ERROR;
@@ -238,6 +239,27 @@ public class ProjectController extends BaseController {
     }
 
     /**
+     * query authorized user
+     *
+     * @param loginUser     login user
+     * @param projectCode   project code
+     * @return users        who have permission for the specified project
+     */
+    @ApiOperation(value = "queryAuthorizedUser", notes = "QUERY_AUTHORIZED_USER_NOTES")
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "projectCode", value = "PROJECT_CODE", dataType = "Long", example = "100")
+    })
+    @GetMapping(value = "/authed-user")
+    @ResponseStatus(HttpStatus.OK)
+    @ApiException(QUERY_AUTHORIZED_USER)
+    @AccessLogAnnotation(ignoreRequestArgs = "loginUser")
+    public Result queryAuthorizedUser(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+            @RequestParam("projectCode") Long projectCode) {
+        Map<String, Object> result = this.projectService.queryAuthorizedUser(loginUser, projectCode);
+        return this.returnDataList(result);
+    }
+
+    /**
      * query authorized and user created project
      *
      * @param loginUser login 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 11de038..a79e5f7 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
@@ -17,6 +17,7 @@
 
 package org.apache.dolphinscheduler.api.controller;
 
+import static org.apache.dolphinscheduler.api.enums.Status.REVOKE_PROJECT_ERROR;
 import static org.apache.dolphinscheduler.api.enums.Status.AUTHORIZED_USER_ERROR;
 import static org.apache.dolphinscheduler.api.enums.Status.CREATE_USER_ERROR;
 import static org.apache.dolphinscheduler.api.enums.Status.DELETE_USER_BY_ID_ERROR;
@@ -235,6 +236,54 @@ public class UsersController extends BaseController {
     }
 
     /**
+     * grant project by code
+     *
+     * @param loginUser login user
+     * @param userId user id
+     * @param projectCode project code
+     * @return grant result code
+     */
+    @ApiOperation(value = "grantProjectByCode", notes = "GRANT_PROJECT_BY_CODE_NOTES")
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "userId", value = "USER_ID", required = true, dataType = "Int", example = "100"),
+        @ApiImplicitParam(name = "projectCode", value = "PROJECT_CODE", required = true, type = "Long")
+    })
+    @PostMapping(value = "/grant-project-by-code")
+    @ResponseStatus(HttpStatus.OK)
+    @ApiException(GRANT_PROJECT_ERROR)
+    @AccessLogAnnotation
+    public Result grantProjectByCode(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+            @RequestParam(value = "userId") int userId,
+            @RequestParam(value = "projectCode") long projectCode) {
+        Map<String, Object> result = this.usersService.grantProjectByCode(loginUser, userId, projectCode);
+        return this.returnDataList(result);
+    }
+
+    /**
+     * revoke project
+     *
+     * @param loginUser     login user
+     * @param userId        user id
+     * @param projectCode   project code
+     * @return revoke result code
+     */
+    @ApiOperation(value = "revokeProject", notes = "REVOKE_PROJECT_NOTES")
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "userId", value = "USER_ID", required = true, dataType = "Int", example = "100"),
+        @ApiImplicitParam(name = "projectCode", value = "PROJECT_CODE", required = true, type = "Long", example = "100")
+    })
+    @PostMapping(value = "/revoke-project")
+    @ResponseStatus(HttpStatus.OK)
+    @ApiException(REVOKE_PROJECT_ERROR)
+    @AccessLogAnnotation
+    public Result revokeProject(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+            @RequestParam(value = "userId") int userId,
+            @RequestParam(value = "projectCode") long projectCode) {
+        Map<String, Object> result = this.usersService.revokeProject(loginUser, userId, projectCode);
+        return returnDataList(result);
+    }
+
+    /**
      * grant resource
      *
      * @param loginUser login user
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 7276404..c4243f0 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
@@ -212,7 +212,8 @@ public enum Status {
     TRANSFORM_PROJECT_OWNERSHIP(10179, "Please transform project ownership [{0}]", "请先转移项目所有权[{0}]"),
     QUERY_ALERT_GROUP_ERROR(10180, "query alert group error", "查询告警组错误"),
     CURRENT_LOGIN_USER_TENANT_NOT_EXIST(10181, "the tenant of the currently login user is not specified", "未指定当前登录用户的租户"),
-
+    REVOKE_PROJECT_ERROR(10182, "revoke project error", "撤销项目授权错误"),
+    QUERY_AUTHORIZED_USER(10183, "query authorized user error", "查询拥有项目权限的用户错误"),
 
     UDF_FUNCTION_NOT_EXIST(20001, "UDF function not found", "UDF函数不存在"),
     UDF_FUNCTION_EXISTS(20002, "UDF function already exists", "UDF函数已存在"),
@@ -304,6 +305,7 @@ public enum Status {
     UPDATE_ACCESS_TOKEN_ERROR(70013, "update access token error", "更新访问token错误"),
     DELETE_ACCESS_TOKEN_ERROR(70014, "delete access token error", "删除访问token错误"),
     ACCESS_TOKEN_NOT_EXIST(70015, "access token not exist", "访问token不存在"),
+    QUERY_ACCESSTOKEN_BY_USER_ERROR(70016, "query access token by user error", "查询访问指定用户的token错误"),
 
 
     COMMAND_STATE_COUNT_ERROR(80001, "task instance state count error", "查询各状态任务实例数错误"),
diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/AccessTokenService.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/AccessTokenService.java
index fc4ed78..b3e14ee 100644
--- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/AccessTokenService.java
+++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/AccessTokenService.java
@@ -39,6 +39,15 @@ public interface AccessTokenService {
     Result queryAccessTokenList(User loginUser, String searchVal, Integer pageNo, Integer pageSize);
 
     /**
+     * query access token for specified user
+     *
+     * @param loginUser login user
+     * @param userId user id
+     * @return token list for specified user
+     */
+    Map<String, Object> queryAccessTokenByUser(User loginUser, Integer userId);
+
+    /**
      * create token
      *
      * @param userId token for user
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 dffa866..1300834 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
@@ -111,6 +111,15 @@ public interface ProjectService {
     Map<String, Object> queryAuthorizedProject(User loginUser, Integer userId);
 
     /**
+     * query authorized user
+     *
+     * @param loginUser     login user
+     * @param projectCode   project code
+     * @return users        who have permission for the specified project
+     */
+    Map<String, Object> queryAuthorizedUser(User loginUser, Long projectCode);
+
+    /**
      * query authorized project
      *
      * @param loginUser login user
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 a5c004b..485702a 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
@@ -154,6 +154,25 @@ public interface UsersService {
 
 
     /**
+     * grant project by code
+     *
+     * @param loginUser login user
+     * @param userId user id
+     * @param projectCode project code
+     * @return grant result code
+     */
+    Map<String, Object> grantProjectByCode(User loginUser, int userId, long projectCode);
+
+    /**
+     * revoke the project permission for specified user.
+     * @param loginUser     Login user
+     * @param userId        User id
+     * @param projectCode   Project Code
+     * @return
+     */
+    Map<String, Object> revokeProject(User loginUser, int userId, long projectCode);
+
+    /**
      * grant resource
      *
      * @param loginUser login user
diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/AccessTokenServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/AccessTokenServiceImpl.java
index 3904846..f5e434a 100644
--- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/AccessTokenServiceImpl.java
+++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/AccessTokenServiceImpl.java
@@ -31,6 +31,7 @@ import org.apache.dolphinscheduler.dao.mapper.AccessTokenMapper;
 
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.slf4j.Logger;
@@ -79,6 +80,30 @@ public class AccessTokenServiceImpl extends BaseServiceImpl implements AccessTok
     }
 
     /**
+     * query access token for specified user
+     *
+     * @param loginUser login user
+     * @param userId user id
+     * @return token list for specified user
+     */
+    @Override
+    public Map<String, Object> queryAccessTokenByUser(User loginUser, Integer userId) {
+        Map<String, Object> result = new HashMap<>();
+        result.put(Constants.STATUS, false);
+
+        // only admin can operate
+        if (isNotAdmin(loginUser, result)) {
+            return result;
+        }
+
+        // query access token for specified user
+        List<AccessToken> accessTokenList = this.accessTokenMapper.queryAccessTokenByUser(userId);
+        result.put(Constants.DATA_LIST, accessTokenList);
+        this.putMsg(result, Status.SUCCESS);
+        return result;
+    }
+
+    /**
      * create token
      *
      * @param userId token for user
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 a02458a..3bb222b 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
@@ -390,6 +390,31 @@ public class ProjectServiceImpl extends BaseServiceImpl implements ProjectServic
     }
 
     /**
+     * query authorized user
+     *
+     * @param loginUser     login user
+     * @param projectCode   project code
+     * @return users        who have permission for the specified project
+     */
+    @Override
+    public Map<String, Object> queryAuthorizedUser(User loginUser, Long projectCode) {
+        Map<String, Object> result = new HashMap<>();
+
+        // 1. check read permission
+        Project project = this.projectMapper.queryByCode(projectCode);
+        boolean hasProjectAndPerm = this.hasProjectAndPerm(loginUser, project, result);
+        if (!hasProjectAndPerm) {
+            return result;
+        }
+
+        // 2. query authorized user list
+        List<User> users = this.userMapper.queryAuthedUserListByProjectId(project.getId());
+        result.put(Constants.DATA_LIST, users);
+        this.putMsg(result, Status.SUCCESS);
+        return result;
+    }
+
+    /**
      * query authorized project
      *
      * @param loginUser login user
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 e07516a..ab7a244 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
@@ -580,6 +580,90 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService {
     }
 
     /**
+     * grant project by code
+     *
+     * @param loginUser login user
+     * @param userId user id
+     * @param projectCode project code
+     * @return grant result code
+     */
+    @Override
+    public Map<String, Object> grantProjectByCode(final User loginUser, final int userId, final long projectCode) {
+        Map<String, Object> result = new HashMap<>();
+        result.put(Constants.STATUS, false);
+
+        // 1. check if user is existed
+        User tempUser = this.userMapper.selectById(userId);
+        if (tempUser == null) {
+            this.putMsg(result, Status.USER_NOT_EXIST, userId);
+            return result;
+        }
+
+        // 2. check if project is existed
+        Project project = this.projectMapper.queryByCode(projectCode);
+        if (project == null) {
+            this.putMsg(result, Status.PROJECT_NOT_FOUNT, projectCode);
+            return result;
+        }
+
+        // 3. only project owner can operate
+        if (!this.hasPerm(loginUser, project.getUserId())) {
+            this.putMsg(result, Status.USER_NO_OPERATION_PERM);
+            return result;
+        }
+
+        // 4. maintain the relationship between project and user
+        final Date today = new Date();
+        ProjectUser projectUser = new ProjectUser();
+        projectUser.setUserId(userId);
+        projectUser.setProjectId(project.getId());
+        projectUser.setPerm(7);
+        projectUser.setCreateTime(today);
+        projectUser.setUpdateTime(today);
+        this.projectUserMapper.insert(projectUser);
+
+        this.putMsg(result, Status.SUCCESS);
+        return result;
+    }
+
+    /**
+     * revoke the project permission for specified user.
+     * @param loginUser     Login user
+     * @param userId        User id
+     * @param projectCode   Project Code
+     * @return
+     */
+    @Override
+    public Map<String, Object> revokeProject(User loginUser, int userId, long projectCode) {
+        Map<String, Object> result = new HashMap<>();
+        result.put(Constants.STATUS, false);
+
+        // 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;
+        }
+
+        // 3. check if project is existed
+        Project project = this.projectMapper.queryByCode(projectCode);
+        if (project == null) {
+            this.putMsg(result, Status.PROJECT_NOT_FOUNT, projectCode);
+            return result;
+        }
+
+        // 4. delete the relationship between project and user
+        this.projectUserMapper.deleteProjectRelation(project.getId(), user.getId());
+        this.putMsg(result, Status.SUCCESS);
+        return result;
+    }
+
+    /**
      * grant resource
      *
      * @param loginUser login user
diff --git a/dolphinscheduler-api/src/main/resources/i18n/messages.properties b/dolphinscheduler-api/src/main/resources/i18n/messages.properties
index 62d3615..b5b127b 100644
--- a/dolphinscheduler-api/src/main/resources/i18n/messages.properties
+++ b/dolphinscheduler-api/src/main/resources/i18n/messages.properties
@@ -140,10 +140,12 @@ DELETE_PROJECT_BY_ID_NOTES=delete project by id
 QUERY_UNAUTHORIZED_PROJECT_NOTES=query unauthorized project
 QUERY_ALL_PROJECT_LIST_NOTES=query all project list
 QUERY_AUTHORIZED_PROJECT_NOTES=query authorized project
+QUERY_AUTHORIZED_USER_NOTES=query authorized user
 TASK_RECORD_TAG=task record related operation
 QUERY_TASK_RECORD_LIST_PAGING_NOTES=query task record list paging 
 CREATE_TOKEN_NOTES=create token ,note: please login first
 QUERY_ACCESS_TOKEN_LIST_NOTES=query access token list paging
+QUERY_ACCESS_TOKEN_BY_USER_NOTES=query access token for specified user
 SCHEDULE=schedule
 WARNING_TYPE=warning type(sending strategy)
 WARNING_GROUP_ID=warning group id
@@ -221,6 +223,9 @@ UPDATE_USER_NOTES=update user
 DELETE_USER_BY_ID_NOTES=delete user by id
 GRANT_PROJECT_NOTES=GRANT PROJECT 
 PROJECT_IDS=project ids(string format, multiple projects separated by ",")
+GRANT_PROJECT_BY_CODE_NOTES=GRANT PROJECT BY CODE
+REVOKE_PROJECT_NOTES=REVOKE PROJECT FOR USER
+PROJECT_CODE=project codes
 GRANT_RESOURCE_NOTES=grant resource file
 RESOURCE_IDS=resource ids(string format, multiple resources separated by ",")
 GET_USER_INFO_NOTES=get user info 
diff --git a/dolphinscheduler-api/src/main/resources/i18n/messages_en_US.properties b/dolphinscheduler-api/src/main/resources/i18n/messages_en_US.properties
index db705be..3fa384e 100644
--- a/dolphinscheduler-api/src/main/resources/i18n/messages_en_US.properties
+++ b/dolphinscheduler-api/src/main/resources/i18n/messages_en_US.properties
@@ -156,10 +156,12 @@ QUERY_ALL_PROJECT_LIST_NOTES=query all project list
 DELETE_PROJECT_BY_ID_NOTES=delete project by id 
 QUERY_UNAUTHORIZED_PROJECT_NOTES=query unauthorized project
 QUERY_AUTHORIZED_PROJECT_NOTES=query authorized project
+QUERY_AUTHORIZED_USER_NOTES=query authorized user
 TASK_RECORD_TAG=task record related operation
 QUERY_TASK_RECORD_LIST_PAGING_NOTES=query task record list paging 
 CREATE_TOKEN_NOTES=create token ,note: please login first
 QUERY_ACCESS_TOKEN_LIST_NOTES=query access token list paging
+QUERY_ACCESS_TOKEN_BY_USER_NOTES=query access token for specified user
 SCHEDULE=schedule
 WARNING_TYPE=warning type(sending strategy)
 WARNING_GROUP_ID=warning group id
@@ -268,6 +270,9 @@ UPDATE_QUEUE_NOTES=update queue
 DELETE_USER_BY_ID_NOTES=delete user by id
 GRANT_PROJECT_NOTES=GRANT PROJECT 
 PROJECT_IDS=project ids(string format, multiple projects separated by ",")
+GRANT_PROJECT_BY_CODE_NOTES=GRANT PROJECT BY CODE
+REVOKE_PROJECT_NOTES=REVOKE PROJECT FOR USER
+PROJECT_CODE=project codes
 GRANT_RESOURCE_NOTES=grant resource file
 RESOURCE_IDS=resource ids(string format, multiple resources separated by ",")
 GET_USER_INFO_NOTES=get user info 
diff --git a/dolphinscheduler-api/src/main/resources/i18n/messages_zh_CN.properties b/dolphinscheduler-api/src/main/resources/i18n/messages_zh_CN.properties
index ec88f74..e18cdf3 100644
--- a/dolphinscheduler-api/src/main/resources/i18n/messages_zh_CN.properties
+++ b/dolphinscheduler-api/src/main/resources/i18n/messages_zh_CN.properties
@@ -145,10 +145,12 @@ QUERY_ALL_PROJECT_LIST_NOTES=查询所有项目
 DELETE_PROJECT_BY_ID_NOTES=通过ID删除项目
 QUERY_UNAUTHORIZED_PROJECT_NOTES=查询未授权的项目
 QUERY_AUTHORIZED_PROJECT_NOTES=查询授权项目
+QUERY_AUTHORIZED_USER_NOTES=查询拥有项目授权的用户
 TASK_RECORD_TAG=任务记录相关操作
 QUERY_TASK_RECORD_LIST_PAGING_NOTES=分页查询任务记录列表
 CREATE_TOKEN_NOTES=创建token,注意需要先登录
 QUERY_ACCESS_TOKEN_LIST_NOTES=分页查询access token列表
+QUERY_ACCESS_TOKEN_BY_USER_NOTES=查询指定用户的access token
 SCHEDULE=定时
 WARNING_TYPE=发送策略
 WARNING_GROUP_ID=发送组ID
@@ -256,6 +258,9 @@ UPDATE_QUEUE_NOTES=更新队列
 DELETE_USER_BY_ID_NOTES=删除用户通过ID
 GRANT_PROJECT_NOTES=授权项目
 PROJECT_IDS=项目IDS(字符串格式,多个项目以","分割)
+GRANT_PROJECT_BY_CODE_NOTES=授权项目
+REVOKE_PROJECT_NOTES=撤销用户的项目权限
+PROJECT_CODE=项目Code
 GRANT_RESOURCE_NOTES=授权资源文件
 RESOURCE_IDS=资源ID列表(字符串格式,多个资源ID以","分割)
 GET_USER_INFO_NOTES=获取用户信息
diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AccessTokenControllerTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AccessTokenControllerTest.java
index a13d773..0a9d7a3 100644
--- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AccessTokenControllerTest.java
+++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AccessTokenControllerTest.java
@@ -110,6 +110,19 @@ public class AccessTokenControllerTest extends AbstractControllerTest {
     }
 
     @Test
+    public void testQueryAccessTokenByUser() throws Exception {
+        MvcResult mvcResult = this.mockMvc
+                .perform(get("/access-tokens/user/1")
+                .header("sessionId", this.sessionId))
+                .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 testDelAccessTokenById() throws Exception {
         MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
         paramsMap.add("id","13");
diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/ProjectControllerTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/ProjectControllerTest.java
index 0bce72d..118484c 100644
--- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/ProjectControllerTest.java
+++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/ProjectControllerTest.java
@@ -126,6 +126,16 @@ public class ProjectControllerTest {
     }
 
     @Test
+    public void testQueryAuthorizedUser() {
+        Map<String, Object> result = new HashMap<>();
+        this.putMsg(result, Status.SUCCESS);
+
+        Mockito.when(this.projectService.queryAuthorizedUser(this.user, 3682329499136L)).thenReturn(result);
+        Result response = this.projectController.queryAuthorizedUser(this.user, 3682329499136L);
+        Assert.assertEquals(Status.SUCCESS.getCode(), response.getCode().intValue());
+    }
+
+    @Test
     public void testQueryAllProjectList() {
         Map<String, Object> result = new HashMap<>();
         putMsg(result, Status.SUCCESS);
diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/UsersControllerTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/UsersControllerTest.java
index fb4b0ce..248cfea 100644
--- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/UsersControllerTest.java
+++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/UsersControllerTest.java
@@ -109,6 +109,43 @@ public class UsersControllerTest extends AbstractControllerTest {
     }
 
     @Test
+    public void testGrantProjectByCode() throws Exception {
+        MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
+        paramsMap.add("userId", "32");
+        paramsMap.add("projectCode", "3682329499136");
+
+        MvcResult mvcResult = this.mockMvc
+                .perform(post("/users/grant-project-by-code")
+                .header(SESSION_ID, this.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.USER_NOT_EXIST.getCode(), result.getCode().intValue());
+        logger.info(mvcResult.getResponse().getContentAsString());
+    }
+
+    @Test
+    public void testRevokeProject() throws Exception {
+        MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
+        paramsMap.add("userId", "32");
+        paramsMap.add("projectCode", "3682329499136");
+
+        MvcResult mvcResult = this.mockMvc.perform(post("/users/revoke-project")
+                .header(SESSION_ID, this.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.USER_NOT_EXIST.getCode(), result.getCode().intValue());
+        logger.info(mvcResult.getResponse().getContentAsString());
+    }
+
+    @Test
     public void testGrantResource() throws Exception {
         MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
         paramsMap.add("userId","32");
diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/AccessTokenServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/AccessTokenServiceTest.java
index 3b8ef6b..8f8554f 100644
--- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/AccessTokenServiceTest.java
+++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/AccessTokenServiceTest.java
@@ -38,11 +38,13 @@ import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
+import org.assertj.core.util.Lists;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnitRunner;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -80,6 +82,25 @@ public class AccessTokenServiceTest {
     }
 
     @Test
+    public void testQueryAccessTokenByUser() {
+        List<AccessToken> accessTokenList = Lists.newArrayList(this.getEntity());
+        Mockito.when(this.accessTokenMapper.queryAccessTokenByUser(1)).thenReturn(accessTokenList);
+
+        // USER_NO_OPERATION_PERM
+        User user = this.getLoginUser();
+        user.setUserType(UserType.GENERAL_USER);
+        Map<String, Object> result = this.accessTokenService.queryAccessTokenByUser(user, 1);
+        logger.info(result.toString());
+        Assert.assertEquals(Status.USER_NO_OPERATION_PERM, result.get(Constants.STATUS));
+
+        // SUCCESS
+        user.setUserType(UserType.ADMIN_USER);
+        result = this.accessTokenService.queryAccessTokenByUser(user, 1);
+        logger.info(result.toString());
+        Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
+    }
+
+    @Test
     public void testCreateToken() {
 
         when(accessTokenMapper.insert(any(AccessToken.class))).thenReturn(2);
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 6f516b7..de782df 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
@@ -277,6 +277,38 @@ public class ProjectServiceTest {
     }
 
     @Test
+    public void testQueryAuthorizedUser() {
+        final User loginUser = this.getLoginUser();
+
+        // Failure 1: PROJECT_NOT_FOUND
+        Map<String, Object> result = this.projectService.queryAuthorizedUser(loginUser, 3682329499136L);
+        logger.info("FAILURE 1: {}", result.toString());
+        Assert.assertEquals(Status.PROJECT_NOT_FOUNT, result.get(Constants.STATUS));
+
+        // Failure 2: USER_NO_OPERATION_PROJECT_PERM
+        loginUser.setId(100);
+        Mockito.when(this.projectMapper.queryByCode(Mockito.anyLong())).thenReturn(this.getProject());
+        result = this.projectService.queryAuthorizedUser(loginUser, 3682329499136L);
+        logger.info("FAILURE 2: {}", result.toString());
+        Assert.assertEquals(Status.USER_NO_OPERATION_PROJECT_PERM, result.get(Constants.STATUS));
+
+        // SUCCESS
+        loginUser.setUserType(UserType.ADMIN_USER);
+        Mockito.when(this.userMapper.queryAuthedUserListByProjectId(1)).thenReturn(this.getUserList());
+        result = this.projectService.queryAuthorizedUser(loginUser, 3682329499136L);
+        logger.info("SUCCESS 1: {}", result.toString());
+        List<User> users = (List<User>) result.get(Constants.DATA_LIST);
+        Assert.assertTrue(CollectionUtils.isNotEmpty(users));
+
+        loginUser.setId(1);
+        loginUser.setUserType(UserType.GENERAL_USER);
+        result = this.projectService.queryAuthorizedUser(loginUser, 3682329499136L);
+        logger.info("SUCCESS 2: {}", result.toString());
+        users = (List<User>) result.get(Constants.DATA_LIST);
+        Assert.assertTrue(CollectionUtils.isNotEmpty(users));
+    }
+
+    @Test
     public void testQueryCreatedProject() {
 
         User loginUser = getLoginUser();
@@ -366,6 +398,28 @@ public class ProjectServiceTest {
     }
 
     /**
+     * Get general user
+     * @return
+     */
+    private User getGeneralUser() {
+        User user = new User();
+        user.setUserType(UserType.GENERAL_USER);
+        user.setUserName("userTest0001");
+        user.setUserPassword("userTest0001");
+        return user;
+    }
+
+    /**
+     * Get user list
+     * @return
+     */
+    private List<User> getUserList() {
+        List<User> userList = new ArrayList<>();
+        userList.add(this.getGeneralUser());
+        return userList;
+    }
+
+    /**
      * get project user
      */
     private ProjectUser getProjectUser() {
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 e586db8..85daa7e 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
@@ -339,6 +339,74 @@ public class UsersServiceTest {
     }
 
     @Test
+    public void testGrantProjectByCode() {
+        // Mock Project, User
+        final long projectCode = 1L;
+        final int projectCreator = 1;
+        final int authorizer = 100;
+        Mockito.when(this.userMapper.selectById(authorizer)).thenReturn(this.getUser());
+        Mockito.when(this.userMapper.selectById(projectCreator)).thenReturn(this.getUser());
+        Mockito.when(this.projectMapper.queryByCode(projectCode)).thenReturn(this.getProject());
+
+        // ERROR: USER_NOT_EXIST
+        User loginUser = new User();
+        Map<String, Object> result = this.usersService.grantProjectByCode(loginUser, 999, projectCode);
+        logger.info(result.toString());
+        Assert.assertEquals(Status.USER_NOT_EXIST, result.get(Constants.STATUS));
+
+        // ERROR: PROJECT_NOT_FOUNT
+        result = this.usersService.grantProjectByCode(loginUser, authorizer, 999);
+        logger.info(result.toString());
+        Assert.assertEquals(Status.PROJECT_NOT_FOUNT, result.get(Constants.STATUS));
+
+        // ERROR: USER_NO_OPERATION_PERM
+        loginUser.setId(999);
+        loginUser.setUserType(UserType.GENERAL_USER);
+        result = this.usersService.grantProjectByCode(loginUser, authorizer, projectCode);
+        logger.info(result.toString());
+        Assert.assertEquals(Status.USER_NO_OPERATION_PERM, result.get(Constants.STATUS));
+
+        // SUCCESS: USER IS PROJECT OWNER
+        loginUser.setId(projectCreator);
+        loginUser.setUserType(UserType.GENERAL_USER);
+        result = this.usersService.grantProjectByCode(loginUser, authorizer, projectCode);
+        logger.info(result.toString());
+        Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
+
+        // SUCCESS: USER IS ADMINISTRATOR
+        loginUser.setId(999);
+        loginUser.setUserType(UserType.ADMIN_USER);
+        result = this.usersService.grantProjectByCode(loginUser, authorizer, projectCode);
+        logger.info(result.toString());
+        Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
+    }
+
+    @Test
+    public void testRevokeProject() {
+        Mockito.when(this.userMapper.selectById(1)).thenReturn(this.getUser());
+
+        final long projectCode = 3682329499136L;
+
+        // user no permission
+        User loginUser = new User();
+        Map<String, Object> result = this.usersService.revokeProject(loginUser, 1, projectCode);
+        logger.info(result.toString());
+        Assert.assertEquals(Status.USER_NO_OPERATION_PERM, result.get(Constants.STATUS));
+
+        // user not exist
+        loginUser.setUserType(UserType.ADMIN_USER);
+        result = this.usersService.revokeProject(loginUser, 2, projectCode);
+        logger.info(result.toString());
+        Assert.assertEquals(Status.USER_NOT_EXIST, result.get(Constants.STATUS));
+
+        // success
+        Mockito.when(this.projectMapper.queryByCode(Mockito.anyLong())).thenReturn(new Project());
+        result = this.usersService.revokeProject(loginUser, 1, projectCode);
+        logger.info(result.toString());
+        Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
+    }
+
+    @Test
     public void testGrantResources() {
         String resourceIds = "100000,120000";
         when(userMapper.selectById(1)).thenReturn(getUser());
@@ -619,6 +687,22 @@ public class UsersServiceTest {
     }
 
     /**
+     * Get project
+     * @return
+     */
+    private Project getProject() {
+        Project project = new Project();
+        project.setId(1);
+        project.setCode(1L);
+        project.setUserId(1);
+        project.setName("PJ-001");
+        project.setPerm(7);
+        project.setDefCount(0);
+        project.setInstRunningCount(0);
+        return project;
+    }
+
+    /**
      * get user
      */
     private User getGeneralUser() {
diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/AccessTokenMapper.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/AccessTokenMapper.java
index 472ba35..3c73a56 100644
--- a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/AccessTokenMapper.java
+++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/AccessTokenMapper.java
@@ -25,6 +25,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 
+import java.util.List;
+
 /**
  * accesstoken mapper interface
  */
@@ -45,6 +47,14 @@ public interface AccessTokenMapper extends BaseMapper<AccessToken> {
     );
 
     /**
+     * Query access token for specified user
+     *
+     * @param userId userId
+     * @return access token for specified user
+     */
+    List<AccessToken> queryAccessTokenByUser(@Param("userId") int userId);
+
+    /**
      * delete by userId
      *
      * @param userId userId
diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/UserMapper.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/UserMapper.java
index 4418363..9fcb488 100644
--- a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/UserMapper.java
+++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/UserMapper.java
@@ -124,4 +124,11 @@ public interface UserMapper extends BaseMapper<User> {
      * @return user list
      */
     List<User> selectByIds(@Param("ids") List<Integer> ids);
+
+    /**
+     * query authed user list by projectId
+     * @param projectId projectId
+     * @return user list
+     */
+    List<User> queryAuthedUserListByProjectId(@Param("projectId") int projectId);
 }
diff --git a/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/AccessTokenMapper.xml b/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/AccessTokenMapper.xml
index 35312fb..f8c8ad4 100644
--- a/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/AccessTokenMapper.xml
+++ b/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/AccessTokenMapper.xml
@@ -31,6 +31,13 @@
         </if>
         order by t.update_time desc
     </select>
+
+    <select id="queryAccessTokenByUser" resultType="org.apache.dolphinscheduler.dao.entity.AccessToken">
+        select id, user_id, token, expire_time, create_time, update_time
+        from t_ds_access_token
+        where user_id = #{userId}
+    </select>
+
     <delete id="deleteAccessTokenByUserId">
         delete from t_ds_access_token
         where user_id = #{userId}
diff --git a/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/UserMapper.xml b/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/UserMapper.xml
index dec6a51..570e395 100644
--- a/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/UserMapper.xml
+++ b/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/UserMapper.xml
@@ -123,4 +123,12 @@
             #{id}
         </foreach>
     </select>
+    <select id="queryAuthedUserListByProjectId" resultType="org.apache.dolphinscheduler.dao.entity.User">
+        select
+        <include refid="baseSqlV2">
+            <property name="alias" value="u"/>
+        </include>
+        from t_ds_user u, t_ds_relation_project_user rel
+        where u.id = rel.user_id and rel.project_id = #{projectId}
+    </select>
 </mapper>