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

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


The following commit(s) were added to refs/heads/master by this push:
     new 711ef9ebb7e   [fix](auth) restrict skip_catalog_priv_check to SHOW and 
SELECT on catalog privilege checks (#61147)
711ef9ebb7e is described below

commit 711ef9ebb7e7666080221691a8a0310eab208d60
Author: Calvin Kirs <[email protected]>
AuthorDate: Tue Mar 10 12:50:44 2026 +0800

      [fix](auth) restrict skip_catalog_priv_check to SHOW and SELECT on 
catalog privilege checks (#61147)
    
    followup https://github.com/apache/doris/pull/60945
    
      ## What problem does this PR solve?
    
    When `skip_catalog_priv_check` is enabled,
    `AccessControllerManager#checkCtlPriv()` currently skips
    catalog privilege checks for external catalogs with a custom access
    controller.
    
    This behavior is too broad because it also affects non-read-only catalog
    privileges such as `CREATE`
    and `LOAD`, which should still be validated by the default internal
    access controller.
---
 .../main/java/org/apache/doris/common/Config.java  |  11 +-
 .../mysql/privilege/AccessControllerManager.java   |  20 ++--
 .../privilege/AccessControllerManagerTest.java     | 124 ++++++++++++++++++++-
 3 files changed, 145 insertions(+), 10 deletions(-)

diff --git a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java 
b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
index dee7a9d95e0..abeab588dbd 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
@@ -3918,8 +3918,15 @@ public class Config extends ConfigBase {
             "agent tasks health check interval, default is five minutes, no 
health check when less than or equal to 0"
     })
     public static long agent_task_health_check_intervals_ms = 5 * 60 * 1000L; 
// 5 min
-    @ConfField(description = {"是否跳过 catalog 层级的鉴权",
-            "Whether to skip catalog level privilege check"})
+    @ConfField(description = {
+            "是否在 catalog 级权限检查中跳过 FE 内部的 catalog 权限校验。仅对配置了自定义 access 
controller 的外部 "
+                    + "catalog 的 SHOW/SELECT 生效;内部 catalog、未配置自定义 access 
controller 的 catalog,"
+                    + "以及 CREATE/LOAD/ALTER 等其他权限仍按默认逻辑校验",
+            "Whether to skip the FE internal catalog privilege check in 
catalog-level privilege validation. "
+                    + "This only applies to SHOW/SELECT on external catalogs 
with a custom access controller. "
+                    + "Internal catalogs, catalogs without a custom access 
controller, and other privileges such "
+                    + "as CREATE/LOAD/ALTER are still validated by the default 
logic"
+    })
     public static boolean skip_catalog_priv_check = false;
 
     @ConfField(mutable = true, description = {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerManager.java
 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerManager.java
index 28fbe9b843a..4d22444b1d2 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerManager.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerManager.java
@@ -196,13 +196,17 @@ public class AccessControllerManager {
         return checkCtlPriv(ctx.getCurrentUserIdentity(), ctl, wanted);
     }
 
+    private boolean canSkipCatalogPrivCheck(PrivPredicate wanted) {
+        return wanted == PrivPredicate.SHOW || wanted == PrivPredicate.SELECT;
+    }
+
+    private boolean shouldSkipCatalogPrivCheck(PrivPredicate wanted) {
+        return Config.skip_catalog_priv_check && 
canSkipCatalogPrivCheck(wanted);
+    }
+
     public boolean checkCtlPriv(UserIdentity currentUser, String ctl, 
PrivPredicate wanted) {
         boolean hasGlobal = checkGlobalPriv(currentUser, wanted);
-        if (!Config.skip_catalog_priv_check) {
-            // for checking catalog priv, always use InternalAccessController.
-            // because catalog priv is only saved in InternalAccessController.
-            return defaultAccessController.checkCtlPriv(hasGlobal, 
currentUser, ctl, wanted);
-        } else {
+        if (shouldSkipCatalogPrivCheck(wanted)) {
             CatalogIf catalog = 
Env.getCurrentEnv().getCatalogMgr().getCatalog(ctl);
             if (catalog == null) {
                 return false;
@@ -217,10 +221,12 @@ public class AccessControllerManager {
             if (Strings.isNullOrEmpty(className)) {
                 // not set access controller, use internal access controller
                 return defaultAccessController.checkCtlPriv(hasGlobal, 
currentUser, ctl, wanted);
-            } else {
-                return true;
             }
+            return true;
         }
+        // for checking catalog priv, always use InternalAccessController.
+        // because catalog priv is only saved in InternalAccessController.
+        return defaultAccessController.checkCtlPriv(hasGlobal, currentUser, 
ctl, wanted);
     }
 
     // ==== Database ====
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AccessControllerManagerTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AccessControllerManagerTest.java
index 9869e26afee..aa07dad27b4 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AccessControllerManagerTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AccessControllerManagerTest.java
@@ -48,7 +48,7 @@ public class AccessControllerManagerTest {
     }
 
     @Test
-    public void testCheckCtlPrivSkipCatalogPrivCheckWithCustomAccessController(
+    public void 
testCheckCtlPrivSkipCatalogPrivCheckWithCustomAccessControllerForSelect(
             @Injectable CatalogAccessController defaultAccessController,
             @Mocked Env env,
             @Mocked CatalogMgr catalogMgr,
@@ -88,6 +88,47 @@ public class AccessControllerManagerTest {
         Assert.assertTrue(accessControllerManager.checkCtlPriv(userIdentity, 
"custom_catalog", PrivPredicate.SELECT));
     }
 
+    @Test
+    public void 
testCheckCtlPrivSkipCatalogPrivCheckWithCustomAccessControllerForShow(
+            @Injectable CatalogAccessController defaultAccessController,
+            @Mocked Env env,
+            @Mocked CatalogMgr catalogMgr,
+            @Mocked CatalogIf catalog) {
+        AccessControllerManager accessControllerManager = 
createAccessControllerManager(defaultAccessController);
+        UserIdentity userIdentity = 
UserIdentity.createAnalyzedUserIdentWithIp("test_user", "%");
+        Config.skip_catalog_priv_check = true;
+
+        new Expectations() {
+            {
+                defaultAccessController.checkGlobalPriv((UserIdentity) any, 
(PrivPredicate) any);
+                minTimes = 0;
+                result = false;
+
+                Env.getCurrentEnv();
+                minTimes = 0;
+                result = env;
+
+                env.getCatalogMgr();
+                minTimes = 0;
+                result = catalogMgr;
+
+                catalogMgr.getCatalog("custom_catalog");
+                minTimes = 0;
+                result = catalog;
+
+                catalog.isInternalCatalog();
+                minTimes = 0;
+                result = false;
+
+                catalog.getProperties();
+                minTimes = 0;
+                result = 
ImmutableMap.of(CatalogMgr.ACCESS_CONTROLLER_CLASS_PROP, 
"mock.access.controller");
+            }
+        };
+
+        Assert.assertTrue(accessControllerManager.checkCtlPriv(userIdentity, 
"custom_catalog", PrivPredicate.SHOW));
+    }
+
     @Test
     public void 
testCheckCtlPrivSkipCatalogPrivCheckWithoutCustomAccessController(
             @Injectable CatalogAccessController defaultAccessController,
@@ -133,6 +174,87 @@ public class AccessControllerManagerTest {
         Assert.assertFalse(accessControllerManager.checkCtlPriv(userIdentity, 
"custom_catalog", PrivPredicate.SELECT));
     }
 
+    @Test
+    public void testCheckCtlPrivCreateMustCheckDefaultAccessController(
+            @Injectable CatalogAccessController defaultAccessController,
+            @Mocked Env env,
+            @Mocked CatalogMgr catalogMgr) {
+        AccessControllerManager accessControllerManager = 
createAccessControllerManager(defaultAccessController);
+        UserIdentity userIdentity = 
UserIdentity.createAnalyzedUserIdentWithIp("test_user", "%");
+        Config.skip_catalog_priv_check = true;
+
+        new Expectations() {
+            {
+                defaultAccessController.checkGlobalPriv((UserIdentity) any, 
(PrivPredicate) any);
+                minTimes = 0;
+                result = false;
+
+                defaultAccessController.checkCtlPriv(anyBoolean, 
(UserIdentity) any, anyString, (PrivPredicate) any);
+                minTimes = 0;
+                result = true;
+
+                Env.getCurrentEnv();
+                minTimes = 0;
+                result = env;
+
+                env.getCatalogMgr();
+                minTimes = 0;
+                result = catalogMgr;
+
+                catalogMgr.getCatalog("not_exist_catalog");
+                minTimes = 0;
+                result = null;
+            }
+        };
+
+        Assert.assertTrue(accessControllerManager.checkCtlPriv(userIdentity, 
"not_exist_catalog", PrivPredicate.CREATE));
+    }
+
+    @Test
+    public void testCheckCtlPrivLoadMustCheckDefaultAccessController(
+            @Injectable CatalogAccessController defaultAccessController,
+            @Mocked Env env,
+            @Mocked CatalogMgr catalogMgr,
+            @Mocked CatalogIf catalog) {
+        AccessControllerManager accessControllerManager = 
createAccessControllerManager(defaultAccessController);
+        UserIdentity userIdentity = 
UserIdentity.createAnalyzedUserIdentWithIp("test_user", "%");
+        Config.skip_catalog_priv_check = true;
+
+        new Expectations() {
+            {
+                defaultAccessController.checkGlobalPriv((UserIdentity) any, 
(PrivPredicate) any);
+                minTimes = 0;
+                result = false;
+
+                defaultAccessController.checkCtlPriv(anyBoolean, 
(UserIdentity) any, anyString, (PrivPredicate) any);
+                minTimes = 0;
+                result = false;
+
+                Env.getCurrentEnv();
+                minTimes = 0;
+                result = env;
+
+                env.getCatalogMgr();
+                minTimes = 0;
+                result = catalogMgr;
+
+                catalogMgr.getCatalog("custom_catalog");
+                minTimes = 0;
+                result = catalog;
+
+                catalog.isInternalCatalog();
+                minTimes = 0;
+                result = false;
+
+                catalog.getProperties();
+                minTimes = 0;
+                result = 
ImmutableMap.of(CatalogMgr.ACCESS_CONTROLLER_CLASS_PROP, 
"mock.access.controller");
+            }
+        };
+
+        Assert.assertFalse(accessControllerManager.checkCtlPriv(userIdentity, 
"custom_catalog", PrivPredicate.LOAD));
+    }
+
     @Test
     public void testCheckCtlPrivSkipCatalogPrivCheckWhenCatalogNotExist(
             @Injectable CatalogAccessController defaultAccessController,


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to