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

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


The following commit(s) were added to refs/heads/branch-2.1 by this push:
     new ff5a4cb5b9f [enhance](auth)when assigning permissions, the current 
user must have corresponding permissions (#32825) (#33948)
ff5a4cb5b9f is described below

commit ff5a4cb5b9f7081d630890e0c4eab2ade49944cb
Author: Mingyu Chen <morning...@163.com>
AuthorDate: Mon Apr 22 12:08:46 2024 +0800

    [enhance](auth)when assigning permissions, the current user must have 
corresponding permissions (#32825) (#33948)
    
    bp #32825
    Co-authored-by: zhangdong <493738...@qq.com>
---
 .../java/org/apache/doris/analysis/GrantStmt.java  | 183 ++++++++++-----------
 .../java/org/apache/doris/analysis/RevokeStmt.java |   6 +-
 .../java/org/apache/doris/common/ErrorCode.java    |   2 +
 .../apache/doris/mysql/privilege/Privilege.java    |   4 +
 .../apache/doris/datasource/CatalogMgrTest.java    |   4 -
 .../org/apache/doris/mysql/privilege/AuthTest.java |  11 +-
 .../suites/account_p0/test_grant_priv.groovy       |  86 ++++++++++
 .../account_p0/test_grant_priv_resource.groovy     |  57 +++++++
 .../account_p0/test_grant_priv_workload.groovy     |  57 +++++++
 9 files changed, 305 insertions(+), 105 deletions(-)

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 6712eec124a..f752ab7aae9 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
@@ -17,6 +17,7 @@
 
 package org.apache.doris.analysis;
 
+import org.apache.doris.analysis.CompoundPredicate.Operator;
 import org.apache.doris.catalog.AccessPrivilegeWithCols;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.common.AnalysisException;
@@ -24,19 +25,23 @@ import org.apache.doris.common.ErrorCode;
 import org.apache.doris.common.ErrorReport;
 import org.apache.doris.common.FeNameFormat;
 import org.apache.doris.common.UserException;
+import org.apache.doris.mysql.privilege.AccessControllerManager;
 import org.apache.doris.mysql.privilege.Auth.PrivLevel;
 import org.apache.doris.mysql.privilege.ColPrivilegeKey;
+import org.apache.doris.mysql.privilege.PrivBitSet;
 import org.apache.doris.mysql.privilege.PrivPredicate;
 import org.apache.doris.mysql.privilege.Privilege;
 import org.apache.doris.qe.ConnectContext;
 
 import com.google.common.base.Joiner;
 import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -55,11 +60,11 @@ public class GrantStmt extends DdlStmt {
     private ResourcePattern resourcePattern;
     private WorkloadGroupPattern workloadGroupPattern;
     private Set<Privilege> privileges = Sets.newHashSet();
-    //Privilege,ctl,db,table -> cols
+    // Privilege,ctl,db,table -> cols
     private Map<ColPrivilegeKey, Set<String>> colPrivileges = 
Maps.newHashMap();
     // Indicates that these roles are granted to a user
     private List<String> roles;
-    //AccessPrivileges will be parsed into two parts,
+    // AccessPrivileges will be parsed into two parts,
     // with the column permissions section placed in "colPrivileges" and the 
others in "privileges"
     private List<AccessPrivilegeWithCols> accessPrivileges;
 
@@ -164,11 +169,11 @@ public class GrantStmt extends DdlStmt {
         }
 
         if (tblPattern != null) {
-            checkTablePrivileges(privileges, role, tblPattern, colPrivileges);
+            checkTablePrivileges(privileges, tblPattern, colPrivileges);
         } else if (resourcePattern != null) {
-            checkResourcePrivileges(privileges, role, resourcePattern);
+            checkResourcePrivileges(privileges, resourcePattern);
         } else if (workloadGroupPattern != null) {
-            checkWorkloadGroupPrivileges(privileges, role, 
workloadGroupPattern);
+            checkWorkloadGroupPrivileges(privileges, workloadGroupPattern);
         } else if (roles != null) {
             checkRolePrivileges();
         }
@@ -187,134 +192,124 @@ public class GrantStmt extends DdlStmt {
 
     /**
      * Rules:
-     * 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'
-     * 5.4 or user should has 'workload group' GRANT_PRIV if grant/revoke 
to/from certain 'workload group'
-     * 6. Can not grant USAGE_PRIV to database or table
+     * 1. some privs in Privilege.notBelongToTablePrivileges can not 
granted/revoked on table
+     * 2. ADMIN_PRIV and NODE_PRIV can only be granted/revoked on GLOBAL level
+     * 3. Only the user with NODE_PRIV can grant NODE_PRIV to other user
+     * 4. Check that the current user has both grant_priv and the permissions 
to be assigned to others
+     * 5. col priv must assign to specific table
      *
      * @param privileges
-     * @param role
      * @param tblPattern
      * @throws AnalysisException
      */
-    public static void checkTablePrivileges(Collection<Privilege> privileges, 
String role, TablePattern tblPattern,
+    public static void checkTablePrivileges(Collection<Privilege> privileges, 
TablePattern tblPattern,
             Map<ColPrivilegeKey, Set<String>> colPrivileges)
             throws AnalysisException {
         // Rule 1
+        checkIncorrectPrivilege(Privilege.notBelongToTablePrivileges, 
privileges);
+        // Rule 2
         if (tblPattern.getPrivLevel() != PrivLevel.GLOBAL && 
(privileges.contains(Privilege.ADMIN_PRIV)
                 || privileges.contains(Privilege.NODE_PRIV))) {
             throw new AnalysisException("ADMIN_PRIV and NODE_PRIV can only be 
granted/revoke on/from *.*.*");
         }
 
-        // Rule 2
+        // Rule 3
         if (privileges.contains(Privilege.NODE_PRIV) && 
!Env.getCurrentEnv().getAccessManager()
                 .checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.OPERATOR)) {
             throw new AnalysisException("Only user with NODE_PRIV can 
grant/revoke NODE_PRIV to other user");
         }
 
-        if (role != null) {
-            // Rule 3 and 4
-            if 
(!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.GRANT)) {
-                
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"GRANT/ROVOKE");
-            }
-        } else {
-            // Rule 5.1 and 5.2
-            if (tblPattern.getPrivLevel() == PrivLevel.GLOBAL) {
-                if (!Env.getCurrentEnv().getAccessManager()
-                        .checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.GRANT)) {
-                    
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"GRANT/ROVOKE");
-                }
-            } else if (tblPattern.getPrivLevel() == PrivLevel.CATALOG) {
-                if 
(!Env.getCurrentEnv().getAccessManager().checkCtlPriv(ConnectContext.get(),
-                        tblPattern.getQualifiedCtl(), PrivPredicate.GRANT)) {
-                    
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"GRANT/ROVOKE");
-                }
-            } else if (tblPattern.getPrivLevel() == PrivLevel.DATABASE) {
-                if 
(!Env.getCurrentEnv().getAccessManager().checkDbPriv(ConnectContext.get(),
-                        tblPattern.getQualifiedCtl(), 
tblPattern.getQualifiedDb(), PrivPredicate.GRANT)) {
-                    
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"GRANT/ROVOKE");
-                }
-            } else {
-                // table level
-                if (!Env.getCurrentEnv().getAccessManager()
-                        .checkTblPriv(ConnectContext.get(), 
tblPattern.getQualifiedCtl(), tblPattern.getQualifiedDb(),
-                                tblPattern.getTbl(), PrivPredicate.GRANT)) {
-                    
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"GRANT/ROVOKE");
-                }
-            }
-        }
-
-        // Rule 6
-        if (privileges.contains(Privilege.USAGE_PRIV)) {
-            throw new AnalysisException("Can not grant/revoke USAGE_PRIV 
to/from database or table");
+        // Rule 4
+        PrivPredicate predicate = getPrivPredicate(privileges);
+        AccessControllerManager accessManager = 
Env.getCurrentEnv().getAccessManager();
+        if (!accessManager.checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.ADMIN)
+                && !checkTablePriv(ConnectContext.get(), predicate, 
tblPattern)) {
+            
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ALL_ACCESS_DENIED_ERROR,
+                    predicate.getPrivs().toPrivilegeList());
         }
 
-        // Rule 7
+        // Rule 5
         if (!MapUtils.isEmpty(colPrivileges) && 
"*".equals(tblPattern.getTbl())) {
             throw new AnalysisException("Col auth must specify specific 
table");
         }
     }
 
-    public static void checkResourcePrivileges(Collection<Privilege> 
privileges, String role,
-            ResourcePattern resourcePattern) throws AnalysisException {
-        for (int i = 0; i < Privilege.notBelongToResourcePrivileges.length; 
i++) {
-            if 
(privileges.contains(Privilege.notBelongToResourcePrivileges[i])) {
+    private static void checkIncorrectPrivilege(Privilege[] 
incorrectPrivileges,
+            Collection<Privilege> privileges) throws AnalysisException {
+        for (int i = 0; i < incorrectPrivileges.length; i++) {
+            if (privileges.contains(incorrectPrivileges[i])) {
                 throw new AnalysisException(
-                        String.format("Can not grant/revoke %s on resource 
to/from any other users or roles",
-                                Privilege.notBelongToResourcePrivileges[i]));
+                        String.format("Can not grant/revoke %s to/from any 
other users or roles",
+                                incorrectPrivileges[i]));
             }
         }
+    }
 
-        if (role != null) {
-            // Rule 3 and 4
-            if 
(!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.GRANT)) {
-                
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"GRANT/ROVOKE");
-            }
-        } else {
-            // Rule 5.1 and 5.3
-            if (resourcePattern.getPrivLevel() == PrivLevel.GLOBAL) {
-                if (!Env.getCurrentEnv().getAccessManager()
-                        .checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.GRANT)) {
-                    
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"GRANT/ROVOKE");
-                }
-            } else {
-                if 
(!Env.getCurrentEnv().getAccessManager().checkResourcePriv(ConnectContext.get(),
-                        resourcePattern.getResourceName(), 
PrivPredicate.GRANT)) {
-                    
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"GRANT/ROVOKE");
-                }
-            }
+    private static PrivPredicate getPrivPredicate(Collection<Privilege> 
privileges) {
+        ArrayList<Privilege> privs = Lists.newArrayList(privileges);
+        privs.add(Privilege.GRANT_PRIV);
+        return PrivPredicate.of(PrivBitSet.of(privs), Operator.AND);
+    }
+
+    private static boolean checkTablePriv(ConnectContext ctx, PrivPredicate 
wanted,
+            TablePattern tblPattern) {
+        AccessControllerManager accessManager = 
Env.getCurrentEnv().getAccessManager();
+        switch (tblPattern.getPrivLevel()) {
+            case GLOBAL:
+                return accessManager.checkGlobalPriv(ctx, wanted);
+            case CATALOG:
+                return accessManager.checkCtlPriv(ConnectContext.get(),
+                        tblPattern.getQualifiedCtl(), wanted);
+            case DATABASE:
+                return accessManager.checkDbPriv(ConnectContext.get(),
+                        tblPattern.getQualifiedCtl(), 
tblPattern.getQualifiedDb(), wanted);
+            default:
+                return accessManager.checkTblPriv(ConnectContext.get(), 
tblPattern.getQualifiedCtl(),
+                        tblPattern.getQualifiedDb(), tblPattern.getTbl(), 
wanted);
         }
     }
 
-    public static void checkWorkloadGroupPrivileges(Collection<Privilege> 
privileges, String role,
-            WorkloadGroupPattern workloadGroupPattern) throws 
AnalysisException {
-        for (int i = 0; i < 
Privilege.notBelongToWorkloadGroupPrivileges.length; i++) {
-            if 
(privileges.contains(Privilege.notBelongToWorkloadGroupPrivileges[i])) {
-                throw new AnalysisException(
-                        String.format("Can not grant/revoke %s on workload 
group to/from any other users or roles",
-                                
Privilege.notBelongToWorkloadGroupPrivileges[i]));
-            }
+    public static void checkResourcePrivileges(Collection<Privilege> 
privileges,
+            ResourcePattern resourcePattern) throws AnalysisException {
+        checkIncorrectPrivilege(Privilege.notBelongToResourcePrivileges, 
privileges);
+
+        PrivPredicate predicate = getPrivPredicate(privileges);
+        AccessControllerManager accessManager = 
Env.getCurrentEnv().getAccessManager();
+        if (!accessManager.checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.ADMIN)
+                && !checkResourcePriv(ConnectContext.get(), resourcePattern, 
predicate)) {
+            
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ALL_ACCESS_DENIED_ERROR,
+                    predicate.getPrivs().toPrivilegeList());
         }
 
-        if (role != null) {
-            // Rule 4
-            if 
(!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.GRANT)) {
-                
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"GRANT/ROVOKE");
-            }
-        } else if 
(!Env.getCurrentEnv().getAccessManager().checkWorkloadGroupPriv(ConnectContext.get(),
-                workloadGroupPattern.getworkloadGroupName(), 
PrivPredicate.GRANT)) {
-            
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"GRANT/ROVOKE");
+    }
+
+    private static boolean checkResourcePriv(ConnectContext ctx, 
ResourcePattern resourcePattern,
+            PrivPredicate privPredicate) {
+        if (resourcePattern.getPrivLevel() == PrivLevel.GLOBAL) {
+            return Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ctx, 
privPredicate);
+        } else {
+            return Env.getCurrentEnv().getAccessManager()
+                    .checkResourcePriv(ctx, resourcePattern.getResourceName(), 
privPredicate);
+        }
+    }
+
+    public static void checkWorkloadGroupPrivileges(Collection<Privilege> 
privileges,
+            WorkloadGroupPattern workloadGroupPattern) throws 
AnalysisException {
+        checkIncorrectPrivilege(Privilege.notBelongToWorkloadGroupPrivileges, 
privileges);
+
+        PrivPredicate predicate = getPrivPredicate(privileges);
+        AccessControllerManager accessManager = 
Env.getCurrentEnv().getAccessManager();
+        if (!accessManager.checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.ADMIN)
+                && !accessManager.checkWorkloadGroupPriv(ConnectContext.get(),
+                workloadGroupPattern.getworkloadGroupName(), predicate)) {
+            
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ALL_ACCESS_DENIED_ERROR,
+                    predicate.getPrivs().toPrivilegeList());
         }
     }
 
     public static void checkRolePrivileges() throws AnalysisException {
         if 
(!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.GRANT)) {
-            
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"GRANT/ROVOKE");
+            
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"GRANT/REVOKE");
         }
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/RevokeStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/RevokeStmt.java
index 18066b925da..e586da88cc8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/RevokeStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/RevokeStmt.java
@@ -147,11 +147,11 @@ public class RevokeStmt extends DdlStmt {
 
         // Revoke operation obey the same rule as Grant operation. reuse the 
same method
         if (tblPattern != null) {
-            GrantStmt.checkTablePrivileges(privileges, role, tblPattern, 
colPrivileges);
+            GrantStmt.checkTablePrivileges(privileges, tblPattern, 
colPrivileges);
         } else if (resourcePattern != null) {
-            GrantStmt.checkResourcePrivileges(privileges, role, 
resourcePattern);
+            GrantStmt.checkResourcePrivileges(privileges, resourcePattern);
         } else if (workloadGroupPattern != null) {
-            GrantStmt.checkWorkloadGroupPrivileges(privileges, role, 
workloadGroupPattern);
+            GrantStmt.checkWorkloadGroupPrivileges(privileges, 
workloadGroupPattern);
         } else if (roles != null) {
             GrantStmt.checkRolePrivileges();
         }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java
index 0ea4cdf0043..5a348278a15 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java
@@ -76,6 +76,8 @@ public enum ErrorCode {
             + "(current value: %d)"),
     ERR_SPECIFIC_ACCESS_DENIED_ERROR(1227, new byte[]{'4', '2', '0', '0', 
'0'}, "Access denied; you need (at least "
             + "one of) the %s privilege(s) for this operation"),
+    ERR_SPECIFIC_ALL_ACCESS_DENIED_ERROR(1227, new byte[] {'4', '2', '0', '0', 
'0'}, "Access denied; you need all "
+            + " %s privilege(s) for this operation"),
     ERR_LOCAL_VARIABLE(1228, new byte[]{'H', 'Y', '0', '0', '0'}, "Variable 
'%s' is a SESSION variable and can't be "
             + "used with SET GLOBAL"),
     ERR_GLOBAL_VARIABLE(1229, new byte[]{'H', 'Y', '0', '0', '0'}, "Variable 
'%s' is a GLOBAL variable and should be "
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Privilege.java 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Privilege.java
index 54130a5d450..1c4a16c0728 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Privilege.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Privilege.java
@@ -70,6 +70,10 @@ public enum Privilege {
             SHOW_VIEW_PRIV
     };
 
+    public static final Privilege[] notBelongToTablePrivileges = {
+            USAGE_PRIV
+    };
+
     public static Map<Privilege, String> privInDorisToMysql =
             ImmutableMap.<Privilege, String>builder() // No NODE_PRIV and 
ADMIN_PRIV in the mysql
                     .put(SELECT_PRIV, "SELECT")
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/CatalogMgrTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/CatalogMgrTest.java
index 5faa9952b95..4f68c952145 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/datasource/CatalogMgrTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/datasource/CatalogMgrTest.java
@@ -366,10 +366,6 @@ public class CatalogMgrTest extends TestWithFeService {
         SwitchStmt switchHive = (SwitchStmt) parseAndAnalyzeStmt("switch 
hive;", user2Ctx);
         env.changeCatalog(user2Ctx, switchHive.getCatalogName());
         Assert.assertEquals(user2Ctx.getDefaultCatalog(), "hive");
-        // user2 can grant select_priv to tpch.customer
-        GrantStmt user2GrantHiveTable = (GrantStmt) parseAndAnalyzeStmt(
-                "grant select_priv on tpch.customer to 'user2'@'%';", 
user2Ctx);
-        auth.grant(user2GrantHiveTable);
 
         showCatalogSql = "SHOW CATALOGS";
         showStmt = (ShowCatalogStmt) parseAndAnalyzeStmt(showCatalogSql);
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 a96586c1fa9..f5f136e434c 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
@@ -1953,13 +1953,15 @@ public class AuthTest {
         TablePattern tablePattern = new TablePattern("db1", "*");
         GrantStmt grantStmt2 = new GrantStmt(userIdentity, null, tablePattern, 
usagePrivileges);
         ExceptionChecker.expectThrowsWithMsg(AnalysisException.class,
-                "Can not grant/revoke USAGE_PRIV to/from database or table", 
() -> grantStmt2.analyze(analyzer));
+                "Can not grant/revoke Usage_priv to/from any other users or 
roles",
+                () -> grantStmt2.analyze(analyzer));
 
         // 3. grant resource prov to role on db.table
         tablePattern = new TablePattern("db1", "*");
         GrantStmt grantStmt3 = new GrantStmt(userIdentity, "test_role", 
tablePattern, usagePrivileges);
         ExceptionChecker.expectThrowsWithMsg(AnalysisException.class,
-                "Can not grant/revoke USAGE_PRIV to/from database or table", 
() -> grantStmt3.analyze(analyzer));
+                "Can not grant/revoke Usage_priv to/from any other users or 
roles",
+                () -> grantStmt3.analyze(analyzer));
 
         // 4.drop user
         dropUser(userIdentity);
@@ -2255,13 +2257,14 @@ public class AuthTest {
         TablePattern tablePattern = new TablePattern("db1", "*");
         GrantStmt grantStmt2 = new GrantStmt(userIdentity, null, tablePattern, 
usagePrivileges);
         ExceptionChecker.expectThrowsWithMsg(AnalysisException.class,
-                "Can not grant/revoke USAGE_PRIV to/from database or table", 
() -> grantStmt2.analyze(analyzer));
+                "Can not grant/revoke Usage_priv to/from any other users or 
roles", () -> grantStmt2.analyze(analyzer));
 
         // 3. grant workload group prov to role on db.table
         tablePattern = new TablePattern("db1", "*");
         GrantStmt grantStmt3 = new GrantStmt(userIdentity, "test_role", 
tablePattern, usagePrivileges);
         ExceptionChecker.expectThrowsWithMsg(AnalysisException.class,
-                "Can not grant/revoke USAGE_PRIV to/from database or table", 
() -> grantStmt3.analyze(analyzer));
+                "Can not grant/revoke Usage_priv to/from any other users or 
roles", () -> grantStmt3.analyze(analyzer));
+
         // 4.drop user
         dropUser(userIdentity);
 
diff --git a/regression-test/suites/account_p0/test_grant_priv.groovy 
b/regression-test/suites/account_p0/test_grant_priv.groovy
new file mode 100644
index 00000000000..abb95e9d122
--- /dev/null
+++ b/regression-test/suites/account_p0/test_grant_priv.groovy
@@ -0,0 +1,86 @@
+// 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 org.junit.Assert;
+
+suite("test_grant_priv") {
+    def user1 = 'test_grant_priv_user1'
+    def user2 = 'test_grant_priv_user2'
+    def role1 = 'test_grant_priv_role1'
+    def pwd = '123456'
+    def dbName = 'test_grant_priv_db'
+    def tokens = context.config.jdbcUrl.split('/')
+    def url=tokens[0] + "//" + tokens[2] + "/" + dbName + "?"
+
+    sql """drop user if exists ${user1}"""
+    sql """drop user if exists ${user2}"""
+    sql """drop role if exists ${role1}"""
+    sql """DROP DATABASE IF EXISTS ${dbName}"""
+
+    sql """CREATE DATABASE ${dbName}"""
+    sql """CREATE ROLE ${role1}"""
+    sql """CREATE USER '${user1}' IDENTIFIED BY '${pwd}'"""
+    sql """CREATE USER '${user2}' IDENTIFIED BY '${pwd}'"""
+
+    // test only have select_priv, can not grant to other user
+    sql """grant select_priv on ${dbName}.* to ${user1}"""
+    connect(user=user1, password="${pwd}", url=url) {
+        try {
+            sql """grant select_priv on ${dbName}.* to ${user2}"""
+            Assert.fail("can not grant to other user");
+        } catch (Exception e) {
+            log.info(e.getMessage())
+        }
+    }
+
+    // test both have select_priv and grant_priv , can grant to other user
+    sql """grant grant_priv on ${dbName}.* to ${user1}"""
+    connect(user=user1, password="${pwd}", url=url) {
+        try {
+            sql """grant select_priv on ${dbName}.* to ${user2}"""
+        } catch (Exception e) {
+            Assert.fail(e.getMessage());
+        }
+        // test have grant_priv,but not have load_priv, can not grant 
load_priv to other user
+        try {
+            sql """grant load_priv on ${dbName}.* to ${user2}"""
+            Assert.fail("can not grant to other user");
+        } catch (Exception e) {
+            log.info(e.getMessage())
+        }
+        // test have grant_priv, can not grant role to other user
+        try {
+            sql """grant '${role1}' to ${user2}"""
+            Assert.fail("can not grant to other user");
+        } catch (Exception e) {
+            log.info(e.getMessage())
+        }
+    }
+
+    // test have global grant_priv, can grant role to other user
+    sql """grant grant_priv on *.* to ${user1}"""
+    try {
+        sql """grant '${role1}' to ${user2}"""
+    } catch (Exception e) {
+        Assert.fail(e.getMessage());
+    }
+
+    sql """drop user if exists ${user1}"""
+    sql """drop user if exists ${user2}"""
+    sql """drop role if exists ${role1}"""
+    sql """DROP DATABASE IF EXISTS ${dbName}"""
+}
diff --git a/regression-test/suites/account_p0/test_grant_priv_resource.groovy 
b/regression-test/suites/account_p0/test_grant_priv_resource.groovy
new file mode 100644
index 00000000000..f679835e1ca
--- /dev/null
+++ b/regression-test/suites/account_p0/test_grant_priv_resource.groovy
@@ -0,0 +1,57 @@
+// 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 org.junit.Assert;
+
+suite("test_grant_priv_resource") {
+    def user1 = 'test_grant_priv_resource_user1'
+    def user2 = 'test_grant_priv_resource_user2'
+    def pwd = '123456'
+    def resource1 = 'test_grant_priv_resource_resource1'
+    def tokens = context.config.jdbcUrl.split('/')
+    def url=tokens[0] + "//" + tokens[2] + "/" + "information_schema" + "?"
+
+    sql """drop user if exists ${user1}"""
+    sql """drop user if exists ${user2}"""
+
+    sql """CREATE USER '${user1}' IDENTIFIED BY '${pwd}'"""
+    sql """CREATE USER '${user2}' IDENTIFIED BY '${pwd}'"""
+
+    // test only have USAGE_PRIV, can not grant to other user
+    sql """grant USAGE_PRIV on RESOURCE ${resource1} to ${user1}"""
+    connect(user=user1, password="${pwd}", url=url) {
+        try {
+            sql """grant USAGE_PRIV on RESOURCE ${resource1} to ${user2}"""
+            Assert.fail("can not grant to other user");
+        } catch (Exception e) {
+            log.info(e.getMessage())
+        }
+    }
+
+    // test both have USAGE_PRIV and grant_priv , can grant to other user
+    sql """grant grant_priv on RESOURCE * to ${user1}"""
+    connect(user=user1, password="${pwd}", url=url) {
+        try {
+            sql """grant USAGE_PRIV on RESOURCE ${resource1} to ${user2}"""
+        } catch (Exception e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    sql """drop user if exists ${user1}"""
+    sql """drop user if exists ${user2}"""
+}
diff --git a/regression-test/suites/account_p0/test_grant_priv_workload.groovy 
b/regression-test/suites/account_p0/test_grant_priv_workload.groovy
new file mode 100644
index 00000000000..291cfe1fa4c
--- /dev/null
+++ b/regression-test/suites/account_p0/test_grant_priv_workload.groovy
@@ -0,0 +1,57 @@
+// 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 org.junit.Assert;
+
+suite("test_grant_priv_workload") {
+    def user1 = 'test_grant_priv_workload_user1'
+    def user2 = 'test_grant_priv_workload_user2'
+    def pwd = '123456'
+    def workload1 = 'test_grant_priv_workload_workload1'
+    def tokens = context.config.jdbcUrl.split('/')
+    def url=tokens[0] + "//" + tokens[2] + "/" + "information_schema" + "?"
+
+    sql """drop user if exists ${user1}"""
+    sql """drop user if exists ${user2}"""
+
+    sql """CREATE USER '${user1}' IDENTIFIED BY '${pwd}'"""
+    sql """CREATE USER '${user2}' IDENTIFIED BY '${pwd}'"""
+
+    // test only have USAGE_PRIV, can not grant to other user
+    sql """grant USAGE_PRIV on WORKLOAD GROUP ${workload1} to ${user1}"""
+    connect(user=user1, password="${pwd}", url=url) {
+        try {
+            sql """grant USAGE_PRIV on WORKLOAD GROUP ${workload1} to 
${user2}"""
+            Assert.fail("can not grant to other user");
+        } catch (Exception e) {
+            log.info(e.getMessage())
+        }
+    }
+
+    // test both have USAGE_PRIV and grant_priv , can grant to other user
+    sql """grant grant_priv on WORKLOAD GROUP ${workload1} to ${user1}"""
+    connect(user=user1, password="${pwd}", url=url) {
+        try {
+           sql """grant USAGE_PRIV on WORKLOAD GROUP ${workload1} to 
${user2}"""
+        } catch (Exception e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    sql """drop user if exists ${user1}"""
+    sql """drop user if exists ${user2}"""
+}


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

Reply via email to