You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by mo...@apache.org on 2022/06/06 03:04:26 UTC

[incubator-doris] branch master updated: [feature](priv) Support grant node_priv to other user. (#9951)

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

morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 856b421086 [feature](priv) Support grant node_priv to other user. (#9951)
856b421086 is described below

commit 856b4210869a1c295049c942e5699851e073c318
Author: Mingyu Chen <mo...@gmail.com>
AuthorDate: Mon Jun 6 11:04:20 2022 +0800

    [feature](priv) Support grant node_priv to other user. (#9951)
    
    Currently, only the root user has node_priv privileges.
    That is, only the root user can operate the addition and deletion of nodes.
    
    In the original design of Doris, there is an Operator role. This role can have node_priv for node operations.
    
    This PR supports assigning node_priv to users other than root.
    However, only users who have both grant_priv and node_priv can assign node_priv to other users.
    This ensures that only the root user has this permission, and users who are given node_priv
    cannot continue to expand this permission outward.
---
 .../admin-manual/privilege-ldap/user-privilege.md  |  10 +-
 .../admin-manual/privilege-ldap/user-privilege.md  |  12 +-
 .../java/org/apache/doris/analysis/GrantStmt.java  |  22 ++--
 .../org/apache/doris/mysql/privilege/AuthTest.java | 124 ++++++++++++++++++++-
 4 files changed, 156 insertions(+), 12 deletions(-)

diff --git a/docs/en/admin-manual/privilege-ldap/user-privilege.md b/docs/en/admin-manual/privilege-ldap/user-privilege.md
index 36d99b0a3c..329b717ef5 100644
--- a/docs/en/admin-manual/privilege-ldap/user-privilege.md
+++ b/docs/en/admin-manual/privilege-ldap/user-privilege.md
@@ -73,10 +73,18 @@ Doris currently supports the following permissions
 
 	Nodes change permissions. Including FE, BE, BROKER node addition, deletion, offline operations. Currently, this permission can only be granted to Root users.
 
+    The root user has this permission by default.
+
+    Users who have both Grant_priv and Node_priv can grant this privilege to other users.
+
+    This permission can only be granted to the Global level.
+
 2. Grant_priv
 
 	Permissions change permissions. Allow the execution of operations including authorization, revocation, add/delete/change user/role, etc.
 
+    However, a user with this permission can not grant node_priv permission to other users, unless the user itself has node_priv permission.
+
 3. Select_priv
 
 	Read-only access to databases and tables.
@@ -149,7 +157,7 @@ ADMIN\_PRIV and GRANT\_PRIV have the authority of **"grant authority"** at the s
 
 2. It is not supported to delete or change the permissions of default created roles or users.
 
-3. The user of the operator role has one and only one user. Users of admin roles can create multiple.
+3. The user of the operator role has one and only one user, that is, root. Users of admin roles can create multiple.
 
 4. Operational instructions for possible conflicts
 
diff --git a/docs/zh-CN/admin-manual/privilege-ldap/user-privilege.md b/docs/zh-CN/admin-manual/privilege-ldap/user-privilege.md
index 592eb672f5..976be837e1 100644
--- a/docs/zh-CN/admin-manual/privilege-ldap/user-privilege.md
+++ b/docs/zh-CN/admin-manual/privilege-ldap/user-privilege.md
@@ -71,12 +71,18 @@ Doris 目前支持以下几种权限
 
 1. Node_priv
 
-   节点变更权限。包括 FE、BE、BROKER 节点的添加、删除、下线等操作。目前该权限只能授予 Root 用户。
+   节点变更权限。包括 FE、BE、BROKER 节点的添加、删除、下线等操作。
+
+   Root 用户默认拥有该权限。同时拥有 Grant_priv 和 Node_priv 的用户,可以将该权限赋予其他用户。
+
+   该权限只能赋予 Global 级别。
 
 2. Grant_priv
 
    权限变更权限。允许执行包括授权、撤权、添加/删除/变更 用户/角色 等操作。
 
+   但拥有该权限的用户能不赋予其他用户 node_priv 权限,除非用户本身拥有 node_priv 权限。
+
 3. Select_priv
 
    对数据库、表的只读权限。
@@ -136,13 +142,13 @@ ADMIN_PRIV 和 GRANT_PRIV 权限同时拥有**授予权限**的权限,较为
 ## 一些说明
 
 1. Doris 初始化时,会自动创建如下用户和角色:
-   1. operator 角色:该角色拥有 Node_priv 和 Admin_priv,即对Doris的所有权限。后续某个升级版本中,我们可能会将该角色的权限限制为 Node_priv,即仅授予节点变更权限。以满足某些云上部署需求。
+   1. operator 角色:该角色拥有 Node_priv 和 Admin_priv,即对Doris的所有权限。
    2. admin 角色:该角色拥有 Admin_priv,即除节点变更以外的所有权限。
    3. root@'%':root 用户,允许从任意节点登陆,角色为 operator。
    4. admin@'%':admin 用户,允许从任意节点登陆,角色为 admin。
 2. 不支持删除或更改默认创建的角色或用户的权限。
 
-3. operator 角色的用户有且只有一个。admin 角色的用户可以创建多个。
+3. operator 角色的用户有且只有一个,即 Root。admin 角色的用户可以创建多个。
 
 4. 一些可能产生冲突的操作说明
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/GrantStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/GrantStmt.java
index c137b31a19..18621f19b2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/GrantStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/GrantStmt.java
@@ -126,26 +126,34 @@ public class GrantStmt extends DdlStmt {
         }
     }
 
-    /*
+    /**
      * Rules:
-     * 1. Can not grant/revoke NODE_PRIV to/from any other user.
-     * 2. ADMIN_PRIV can only be granted/revoked on GLOBAL level
+     * 1. ADMIN_PRIV and NODE_PRIV can only be granted/revoked on GLOBAL level
+     * 2. Only the user with NODE_PRIV can grant NODE_PRIV to other user
      * 3. Privileges can not be granted/revoked to/from ADMIN and OPERATOR role
      * 4. Only user with GLOBAL level's GRANT_PRIV can grant/revoke privileges to/from roles.
      * 5.1 User should has GLOBAL level GRANT_PRIV
      * 5.2 or user has DATABASE/TABLE level GRANT_PRIV if grant/revoke to/from certain database or table.
      * 5.3 or user should has 'resource' GRANT_PRIV if grant/revoke to/from certain 'resource'
+     *
+     * @param analyzer
+     * @param privileges
+     * @param role
+     * @param tblPattern
+     * @throws AnalysisException
      */
     public static void checkPrivileges(Analyzer analyzer, List<PaloPrivilege> privileges,
                                        String role, TablePattern tblPattern) throws AnalysisException {
         // Rule 1
-        if (privileges.contains(PaloPrivilege.NODE_PRIV)) {
-            throw new AnalysisException("Can not grant NODE_PRIV to any other users or roles");
+        if (tblPattern.getPrivLevel() != PrivLevel.GLOBAL && (privileges.contains(PaloPrivilege.ADMIN_PRIV)
+                || privileges.contains(PaloPrivilege.NODE_PRIV))) {
+            throw new AnalysisException("ADMIN_PRIV and NODE_PRIV can only be granted on *.*");
         }
 
         // Rule 2
-        if (tblPattern.getPrivLevel() != PrivLevel.GLOBAL && privileges.contains(PaloPrivilege.ADMIN_PRIV)) {
-            throw new AnalysisException("ADMIN_PRIV privilege can only be granted on *.*");
+        if (privileges.contains(PaloPrivilege.NODE_PRIV) && !Catalog.getCurrentCatalog().getAuth()
+                .checkGlobalPriv(ConnectContext.get(), PrivPredicate.OPERATOR)) {
+            throw new AnalysisException("Only the user with NODE_PRIV can grant NODE_PRIV to other user");
         }
 
         if (role != null) {
diff --git a/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java b/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java
index c485ffe896..886783896a 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java
@@ -143,7 +143,8 @@ public class AuthTest {
     }
 
     @Test
-    public void test() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+    public void test()
+            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, UserException {
         // 1. create cmy@%
         UserIdentity userIdentity = new UserIdentity("cmy", "%");
         UserDesc userDesc = new UserDesc(userIdentity, "12345", true);
@@ -1207,6 +1208,127 @@ public class AuthTest {
         } catch (UserException e) {
             e.printStackTrace();
         }
+
+        // 40. create new user and grant node_priv to it
+        final UserIdentity opUser = new UserIdentity("op_user", "%");
+        userDesc = new UserDesc(opUser, "12345", true);
+        createUserStmt = new CreateUserStmt(false, userDesc, null);
+        createUserStmt.analyze(analyzer);
+        auth.createUser(createUserStmt);
+
+        privileges = Lists.newArrayList(AccessPrivilege.NODE_PRIV);
+        // 40.1 grant to non-global level, which is not allowed
+        grantStmt = new GrantStmt(opUser, null, new TablePattern("db1", "*"), privileges);
+        try {
+            grantStmt.analyze(analyzer);
+            Assert.fail();
+        } catch (UserException e) {
+            e.printStackTrace();
+        }
+
+        grantStmt = new GrantStmt(opUser, null, new TablePattern("db1", "tbl"), privileges);
+        try {
+            grantStmt.analyze(analyzer);
+            Assert.fail();
+        } catch (UserException e) {
+            e.printStackTrace();
+        }
+        // 40.2 grant to global level
+        new Expectations() {
+            {
+                ctx.getCurrentUserIdentity();
+                minTimes = 1;
+                result = opUser;
+            }
+        };
+        Assert.assertFalse(auth.checkGlobalPriv(ctx, PrivPredicate.OPERATOR));
+        grantStmt = new GrantStmt(opUser, null, new TablePattern("*", "*"), privileges);
+        // first, use op_user itself to grant node_priv, which is not allowed
+        try {
+            new Expectations() {
+                {
+                    ctx.getCurrentUserIdentity();
+                    minTimes = 1;
+                    result = opUser;
+                }
+            };
+            grantStmt.analyze(analyzer);
+            Assert.fail();
+        } catch (UserException e) {
+            e.printStackTrace();
+        }
+        // second, use root to grant node_priv
+        try {
+            new Expectations() {
+                {
+                    ctx.getCurrentUserIdentity();
+                    minTimes = 1;
+                    result = UserIdentity.ROOT;
+                }
+            };
+            grantStmt.analyze(analyzer);
+            auth.grant(grantStmt);
+        } catch (UserException e) {
+            e.printStackTrace();
+            Assert.fail();
+        }
+        // switch to op_user to check it has node_priv
+        new Expectations() {
+            {
+                ctx.getCurrentUserIdentity();
+                minTimes = 2;
+                result = opUser;
+            }
+        };
+        Assert.assertTrue(auth.checkGlobalPriv(ctx, PrivPredicate.OPERATOR));
+        // Now, op_user only has node_priv, it can not grant node_priv to other user.
+        // create otherOpUser first
+        UserIdentity otherOpUser = new UserIdentity("other_op_user", "%");
+        userDesc = new UserDesc(otherOpUser, "12345", true);
+        createUserStmt = new CreateUserStmt(false, userDesc, null);
+        createUserStmt.analyze(analyzer);
+        auth.createUser(createUserStmt);
+        // try grant, it should fail
+        grantStmt = new GrantStmt(otherOpUser, null, new TablePattern("*", "*"), privileges);
+        try {
+            grantStmt.analyze(analyzer);
+            Assert.fail();
+        } catch (UserException e) {
+            e.printStackTrace();
+        }
+        // Now, we grant grant_priv to opUser, and check if it can than grant node_priv to other user
+        privileges = Lists.newArrayList(AccessPrivilege.GRANT_PRIV);
+        grantStmt = new GrantStmt(opUser, null, new TablePattern("*", "*"), privileges);
+        try {
+            new Expectations() {
+                {
+                    ctx.getCurrentUserIdentity();
+                    minTimes = 2;
+                    result = UserIdentity.ROOT;
+                }
+            };
+            grantStmt.analyze(analyzer);
+            auth.grant(grantStmt);
+        } catch (UserException e) {
+            e.printStackTrace();
+            Assert.fail();
+        }
+        // grant node_priv to other_op_user
+        grantStmt = new GrantStmt(otherOpUser, null, new TablePattern("*", "*"), privileges);
+        try {
+            new Expectations() {
+                {
+                    ctx.getCurrentUserIdentity();
+                    minTimes = 1;
+                    result = opUser;
+                }
+            };
+            grantStmt.analyze(analyzer);
+            auth.grant(grantStmt);
+        } catch (UserException e) {
+            e.printStackTrace();
+            Assert.fail();
+        }
     }
 
     @Test


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org