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]