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/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 2dbb0c734fc [fix](priv) skip catalog priv check if using customized
access controller (#60945)
2dbb0c734fc is described below
commit 2dbb0c734fc4dca7a7e097aeabbae2f53ca0c92c
Author: Calvin Kirs <[email protected]>
AuthorDate: Mon Mar 2 22:02:30 2026 +0800
[fix](priv) skip catalog priv check if using customized access controller
(#60945)
---
.../main/java/org/apache/doris/common/Config.java | 3 +
.../mysql/privilege/AccessControllerManager.java | 28 +++-
.../privilege/AccessControllerManagerTest.java | 174 +++++++++++++++++++++
3 files changed, 202 insertions(+), 3 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 c43b89c5184..cd345b5eaad 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
@@ -3910,6 +3910,9 @@ 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"})
+ 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 973513ea4c3..28fbe9b843a 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
@@ -25,6 +25,7 @@ import org.apache.doris.common.Config;
import org.apache.doris.common.UserException;
import org.apache.doris.common.util.ClassLoaderUtils;
import org.apache.doris.datasource.CatalogIf;
+import org.apache.doris.datasource.CatalogMgr;
import org.apache.doris.datasource.ExternalCatalog;
import org.apache.doris.datasource.InternalCatalog;
import org.apache.doris.info.TableNameInfo;
@@ -32,6 +33,7 @@ import org.apache.doris.plugin.PropertiesUtils;
import org.apache.doris.qe.ConnectContext;
import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
@@ -196,9 +198,29 @@ public class AccessControllerManager {
public boolean checkCtlPriv(UserIdentity currentUser, String ctl,
PrivPredicate wanted) {
boolean hasGlobal = checkGlobalPriv(currentUser, wanted);
- // for checking catalog priv, always use InternalAccessController.
- // because catalog priv is only saved in InternalAccessController.
- return defaultAccessController.checkCtlPriv(hasGlobal, currentUser,
ctl, 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 {
+ CatalogIf catalog =
Env.getCurrentEnv().getCatalogMgr().getCatalog(ctl);
+ if (catalog == null) {
+ return false;
+ }
+ if (catalog.isInternalCatalog()) {
+ return defaultAccessController.checkCtlPriv(hasGlobal,
currentUser, ctl, wanted);
+ }
+ // If catalog not set access controller, use internal access
controller
+ // otherwise, skip catalog priv check
+ String className = (String)
catalog.getProperties().getOrDefault(CatalogMgr.ACCESS_CONTROLLER_CLASS_PROP,
+ "");
+ if (Strings.isNullOrEmpty(className)) {
+ // not set access controller, use internal access controller
+ return defaultAccessController.checkCtlPriv(hasGlobal,
currentUser, ctl, wanted);
+ } else {
+ return true;
+ }
+ }
}
// ==== 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
new file mode 100644
index 00000000000..9869e26afee
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AccessControllerManagerTest.java
@@ -0,0 +1,174 @@
+// 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.
+
+package org.apache.doris.mysql.privilege;
+
+import org.apache.doris.analysis.UserIdentity;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.Config;
+import org.apache.doris.common.jmockit.Deencapsulation;
+import org.apache.doris.datasource.CatalogIf;
+import org.apache.doris.datasource.CatalogMgr;
+
+import com.google.common.collect.ImmutableMap;
+import mockit.Expectations;
+import mockit.Injectable;
+import mockit.Mocked;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AccessControllerManagerTest {
+
+ private boolean originalSkipCatalogPrivCheck;
+
+ @Before
+ public void setUp() {
+ originalSkipCatalogPrivCheck = Config.skip_catalog_priv_check;
+ }
+
+ @After
+ public void tearDown() {
+ Config.skip_catalog_priv_check = originalSkipCatalogPrivCheck;
+ }
+
+ @Test
+ public void testCheckCtlPrivSkipCatalogPrivCheckWithCustomAccessController(
+ @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.SELECT));
+ }
+
+ @Test
+ public void
testCheckCtlPrivSkipCatalogPrivCheckWithoutCustomAccessController(
+ @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("type", "test");
+ }
+ };
+
+ Assert.assertFalse(accessControllerManager.checkCtlPriv(userIdentity,
"custom_catalog", PrivPredicate.SELECT));
+ }
+
+ @Test
+ public void testCheckCtlPrivSkipCatalogPrivCheckWhenCatalogNotExist(
+ @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;
+
+ Env.getCurrentEnv();
+ minTimes = 0;
+ result = env;
+
+ env.getCatalogMgr();
+ minTimes = 0;
+ result = catalogMgr;
+
+ catalogMgr.getCatalog("not_exist_catalog");
+ minTimes = 0;
+ result = null;
+ }
+ };
+
+ Assert.assertFalse(accessControllerManager.checkCtlPriv(userIdentity,
+ "not_exist_catalog", PrivPredicate.SELECT));
+ }
+
+ private AccessControllerManager
createAccessControllerManager(CatalogAccessController defaultAccessController) {
+ AccessControllerManager accessControllerManager = new
AccessControllerManager(new Auth());
+ Deencapsulation.setField(accessControllerManager,
"defaultAccessController", defaultAccessController);
+ return accessControllerManager;
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]