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

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


The following commit(s) were added to refs/heads/branch-3.1 by this push:
     new b09efddf8fa branch-3.1: [feat](Authorization-plugin)Authorization 
framework modularization #40750 #41100 #41574 (#52577)
b09efddf8fa is described below

commit b09efddf8fab0413d4452faae911dc283d4c2cb6
Author: Calvin Kirs <[email protected]>
AuthorDate: Wed Jul 2 10:55:29 2025 +0800

    branch-3.1: [feat](Authorization-plugin)Authorization framework 
modularization #40750 #41100 #41574 (#52577)
    
    Cherry-picked from #40750 #41100 #41574
---
 .../main/java/org/apache/doris/common/Config.java  |  21 +++
 .../ranger/doris/RangerDorisAccessController.java  |  19 +--
 .../hive/RangerHiveAccessControllerFactory.java    |   6 +
 .../doris/common/util/ChildFirstClassLoader.java   | 151 +++++++++++++++++++++
 .../apache/doris/common/util/ClassLoaderUtils.java | 126 +++++++++++++++++
 .../apache/doris/common/util/PropertyAnalyzer.java |  11 --
 .../mysql/authenticate/AuthenticatorManager.java   |  39 +++---
 .../mysql/privilege/AccessControllerFactory.java   |   8 ++
 .../mysql/privilege/AccessControllerManager.java   | 104 ++++++++++----
 ...ava => RangerDorisAccessControllerFactory.java} |  13 +-
 .../doris/mysql/privilege/UserPropertyMgr.java     |  68 ++++++----
 .../org/apache/doris/plugin/PropertiesUtils.java   |  68 ++++++++++
 ...e.doris.mysql.privilege.AccessControllerFactory |  19 +++
 .../privileges/CustomAccessControllerFactory.java} |  11 +-
 .../nereids/privileges/TestCheckPrivileges.java    |  23 ++--
 ...e.doris.mysql.privilege.AccessControllerFactory |  18 +++
 16 files changed, 594 insertions(+), 111 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 f884c9e3d85..879e48b204c 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
@@ -3399,4 +3399,25 @@ public class Config extends ConfigBase {
     @ConfField(mutable = true, description = {"Prometheus 输出表维度指标的个数限制",
             "Prometheus output table dimension metric count limit"})
     public static int prom_output_table_metrics_limit = 10000;
+
+    @ConfField(description = {"认证插件目录",
+            "Authentication plugin directory"})
+    public static String authentication_plugins_dir = EnvUtils.getDorisHome() 
+ "/plugins/authentication";
+
+    @ConfField(description = {"鉴权插件目录",
+            "Authorization plugin directory"})
+    public static String authorization_plugins_dir = EnvUtils.getDorisHome() + 
"/plugins/authorization";
+
+    @ConfField(description = {
+            "鉴权插件配置文件路径,需在 DORIS_HOME 下,默认为 conf/authorization.conf",
+            "Authorization plugin configuration file path, need to be in 
DORIS_HOME,"
+                    + "default is conf/authorization.conf"})
+    public static String authorization_config_file_path = 
"/conf/authorization.conf";
+
+    @ConfField(description = {
+            "认证插件配置文件路径,需在 DORIS_HOME 下,默认为 conf/authentication.conf",
+            "Authentication plugin configuration file path, need to be in 
DORIS_HOME,"
+                    + "default is conf/authentication.conf"})
+    public static String authentication_config_file_path = 
"/conf/authentication.conf";
+
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/RangerDorisAccessController.java
 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/RangerDorisAccessController.java
index 359910aba49..9ed9daa08f8 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/RangerDorisAccessController.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/RangerDorisAccessController.java
@@ -46,36 +46,21 @@ public class RangerDorisAccessController extends 
RangerAccessController {
     private static final String GLOBAL_PRIV_FIXED_NAME = "*";
 
     private RangerBasePlugin dorisPlugin;
-
-    private static RangerDorisAccessController instance;
-
     // private static ScheduledThreadPoolExecutor logFlushTimer = 
ThreadPoolManager.newDaemonScheduledThreadPool(1,
     //        "ranger-doris-audit-log-flusher-timer", true);
     // private RangerHiveAuditHandler auditHandler;
 
-    private RangerDorisAccessController(String serviceName) {
+    public RangerDorisAccessController(String serviceName) {
         this(serviceName, null);
     }
 
-    private RangerDorisAccessController(String serviceName, 
RangerAuthContextListener rangerAuthContextListener) {
+    public RangerDorisAccessController(String serviceName, 
RangerAuthContextListener rangerAuthContextListener) {
         dorisPlugin = new RangerDorisPlugin(serviceName, 
rangerAuthContextListener);
         // auditHandler = new RangerHiveAuditHandler(dorisPlugin.getConfig());
         // start a timed log flusher
         // logFlushTimer.scheduleAtFixedRate(new 
RangerHiveAuditLogFlusher(auditHandler), 10, 20L, TimeUnit.SECONDS);
     }
 
-    public static RangerDorisAccessController getInstance(String serviceName) {
-        return getInstance(serviceName, null);
-    }
-
-    public static synchronized RangerDorisAccessController getInstance(String 
serviceName,
-            RangerAuthContextListener rangerAuthContextListener) {
-        if (instance == null) {
-            instance = new RangerDorisAccessController(serviceName, 
rangerAuthContextListener);
-        }
-        return instance;
-    }
-
     @VisibleForTesting
     public RangerDorisAccessController(RangerBasePlugin plugin) {
         dorisPlugin = plugin;
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAccessControllerFactory.java
 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAccessControllerFactory.java
index 3e9f11d9f8e..a45632ff9e6 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAccessControllerFactory.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAccessControllerFactory.java
@@ -23,6 +23,12 @@ import 
org.apache.doris.mysql.privilege.CatalogAccessController;
 import java.util.Map;
 
 public class RangerHiveAccessControllerFactory implements 
AccessControllerFactory {
+
+    @Override
+    public String factoryIdentifier() {
+        return "ranger-hive";
+    }
+
     @Override
     public CatalogAccessController createAccessController(Map<String, String> 
prop) {
         return new RangerHiveAccessController(prop);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/common/util/ChildFirstClassLoader.java
 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/ChildFirstClassLoader.java
new file mode 100644
index 00000000000..ad3b0dfe77d
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/ChildFirstClassLoader.java
@@ -0,0 +1,151 @@
+// 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.common.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * ChildFirstClassLoader is a custom class loader designed to load classes from
+ * plugin JAR files. It uses a child-first class loading strategy, where the 
loader
+ * first attempts to load classes from its own URLs (plugin JARs), and if the 
class
+ * is not found, it delegates the loading to its parent class loader.
+ * <p>
+ * This class is intended for plugin-based systems where classes defined in 
plugins
+ * might override or replace standard library classes.
+ * <p>
+ * Key features:
+ * - Child-First loading mechanism.
+ * - Support for loading classes from multiple JAR files.
+ * - Efficient caching of JAR file resources to avoid repeated file access.
+ */
+public class ChildFirstClassLoader extends URLClassLoader {
+
+    // A list of URLs pointing to JAR files
+    private final List<URL> jarURLs;
+
+    /**
+     * Constructs a new ChildFirstClassLoader with the given URLs and parent 
class loader.
+     * This constructor stores the URLs for class loading.
+     *
+     * @param urls   The URLs pointing to the plugin JAR files.
+     * @param parent The parent class loader to use for delegation if class is 
not found.
+     * @throws IOException        If there is an error opening the JAR files.
+     * @throws URISyntaxException If there is an error converting the URL to 
URI.
+     */
+    public ChildFirstClassLoader(URL[] urls, ClassLoader parent) throws 
IOException, URISyntaxException {
+        super(urls, parent);
+        this.jarURLs = new ArrayList<>();
+        for (URL url : urls) {
+            if ("file".equals(url.getProtocol())) {
+                this.jarURLs.add(url);
+            }
+        }
+    }
+
+    /**
+     * Attempts to load the class with the specified name.
+     * This method first tries to find the class using the current class 
loader (child-first strategy),
+     * and if the class is not found, it delegates the loading to the parent 
class loader.
+     *
+     * @param name    The fully qualified name of the class to be loaded.
+     * @param resolve If true, the class will be resolved after being loaded.
+     * @return The resulting Class object.
+     * @throws ClassNotFoundException If the class cannot be found by either 
the child or parent loader.
+     */
+    @Override
+    protected Class<?> loadClass(String name, boolean resolve) throws 
ClassNotFoundException {
+        // Child-First mechanism: try to find the class locally first
+        try {
+            return findClass(name);
+        } catch (ClassNotFoundException e) {
+            // If the class is not found locally, delegate to the parent class 
loader
+            return super.loadClass(name, resolve);
+        }
+    }
+
+    /**
+     * Searches for the class in the loaded plugin JAR files.
+     * If the class is found in one of the JAR files, it will be defined and 
returned.
+     *
+     * @param name The fully qualified name of the class to find.
+     * @return The resulting Class object.
+     * @throws ClassNotFoundException If the class cannot be found in the JAR 
files.
+     */
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        String classFile = name.replace('.', '/') + ".class";  // Convert 
class name to path
+
+        // Iterate over all the JAR URLs to find the class
+        for (URL jarURL : jarURLs) {
+            try (JarFile jarFile = new 
JarFile(Paths.get(jarURL.toURI()).toFile())) {
+                JarEntry entry = jarFile.getJarEntry(classFile);
+                if (entry != null) {
+                    try (InputStream inputStream = 
jarFile.getInputStream(entry)) {
+                        byte[] classData = readAllBytes(inputStream);
+                        // Define the class from the byte array
+                        return defineClass(name, classData, 0, 
classData.length);
+                    }
+                }
+            } catch (IOException | URISyntaxException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        // If the class was not found in any JAR file, throw 
ClassNotFoundException
+        throw new ClassNotFoundException(name);
+    }
+
+    /**
+     * Reads all bytes from the given InputStream.
+     * This method reads the entire content of the InputStream and returns it 
as a byte array.
+     *
+     * @param inputStream The InputStream to read from.
+     * @return A byte array containing the data from the InputStream.
+     * @throws IOException If an I/O error occurs while reading the stream.
+     */
+    private byte[] readAllBytes(InputStream inputStream) throws IOException {
+        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) 
{
+            byte[] buffer = new byte[1024];
+            int bytesRead;
+            while ((bytesRead = inputStream.read(buffer)) != -1) {
+                outputStream.write(buffer, 0, bytesRead);
+            }
+            return outputStream.toByteArray();
+        }
+    }
+
+    /**
+     * Closes all open JAR files and releases any resources held by this class 
loader.
+     * This method should be called when the class loader is no longer needed 
to avoid resource leaks.
+     *
+     * @throws IOException If an I/O error occurs while closing the JAR files.
+     */
+    @Override
+    public void close() throws IOException {
+        super.close();  // Call the superclass close method
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/common/util/ClassLoaderUtils.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/ClassLoaderUtils.java
new file mode 100644
index 00000000000..c82858c7d01
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/ClassLoaderUtils.java
@@ -0,0 +1,126 @@
+// 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.common.util;
+
+import org.apache.doris.common.Config;
+import org.apache.doris.mysql.authenticate.AuthenticatorFactory;
+import org.apache.doris.mysql.privilege.AccessControllerFactory;
+
+import org.apache.commons.collections.map.HashedMap;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+/**
+ * Utility class for loading service implementations from external JAR files 
in specific plugin directories.
+ * <p>
+ * This class provides a mechanism to dynamically load service implementations 
from JAR files located in
+ * plugin directories, which are mapped by the service type's simple name. It 
uses a child-first class loading
+ * strategy to ensure that plugins in the JAR files are prioritized over 
classes loaded by the parent class loader.
+ * <p>
+ * It is particularly useful in scenarios where the system needs to support 
modular or pluggable architectures,
+ * such as dynamically loading authenticators, access controllers, or other 
pluggable services from external
+ * directories without requiring the services to be bundled with the core 
application.
+ * <p>
+ * Plugin directory mappings are maintained in a static map where the key is 
the simple name of the service class,
+ * and the value is the relative path to the directory containing the plugin 
JARs.
+ * <p>
+ * Example usage:
+ * <pre>
+ * {@code
+ * List<AuthenticatorFactory> authenticators = 
ClassLoaderUtils.loadServicesFromDirectory(AuthenticatorFactory.class);
+ * }
+ * </pre>
+ *
+ * @see ServiceLoader
+ * @see ChildFirstClassLoader
+ */
+public class ClassLoaderUtils {
+    private static final Logger LOG = 
LogManager.getLogger(ClassLoaderUtils.class);
+    // A mapping of service class simple names to their respective plugin 
directories.
+    private static final Map<String, String> pluginDirMapping = new 
HashedMap();
+
+    static {
+        pluginDirMapping.put(AuthenticatorFactory.class.getSimpleName(), 
Config.authentication_plugins_dir);
+        pluginDirMapping.put(AccessControllerFactory.class.getSimpleName(), 
Config.authorization_plugins_dir);
+    }
+
+    /**
+     * Loads service implementations from JAR files in the specified plugin 
directory.
+     * <p>
+     * The method first looks up the directory for the given service class 
type from the {@code pluginDirMapping}.
+     * If a directory exists and contains JAR files, it will load the service 
implementations from those JAR files
+     * using a child-first class loader to prioritize the plugin classes.
+     * <p>
+     * If no directory is found for the service type, or the directory is 
invalid, an exception is thrown. If the
+     * directory does not contain any JAR files, an empty list is returned.
+     *
+     * @param serviceClass The class type of the service to load. This should 
be the interface or
+     *                     base class of the service.
+     * @param <T>          The type of the service.
+     * @return A list of service instances loaded from JAR files. If no 
services are found, an empty list is returned.
+     * @throws IOException      If there is an error reading the JAR files or 
the directory is invalid.
+     * @throws RuntimeException If there is a problem with the directory 
mapping or JAR file URL creation.
+     */
+    public static <T> List<T> loadServicesFromDirectory(Class<T> serviceClass) 
throws IOException {
+        String pluginDirKey = serviceClass.getSimpleName();
+        String pluginDir = pluginDirMapping.get(pluginDirKey);
+        if (pluginDir == null) {
+            throw new RuntimeException("No mapping found for plugin directory 
key: " + pluginDirKey);
+        }
+        File jarDir = new File(pluginDir);
+        // If the directory does not exist, return an empty list.
+        if (!jarDir.exists()) {
+            return new ArrayList<>();
+        }
+        if (!jarDir.isDirectory()) {
+            throw new IOException("The specified path is not a directory: " + 
pluginDir);
+        }
+
+        File[] jarFiles = jarDir.listFiles((dir, name) -> 
name.endsWith(".jar"));
+        if (jarFiles == null || jarFiles.length == 0) {
+            LOG.info("No JAR files found in the plugin directory: {}", 
pluginDir);
+            return new ArrayList<>();
+        }
+
+        List<T> services = new ArrayList<>();
+        for (File jarFile : jarFiles) {
+            URL[] jarURLs;
+            jarURLs = new URL[]{jarFile.toURI().toURL()};
+
+            try (ChildFirstClassLoader urlClassLoader = new 
ChildFirstClassLoader(jarURLs,
+                    Thread.currentThread().getContextClassLoader())) {
+                ServiceLoader<T> serviceLoader = 
ServiceLoader.load(serviceClass, urlClassLoader);
+                for (T service : serviceLoader) {
+                    services.add(service);
+                }
+            } catch (URISyntaxException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return services;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java
index dbe9eb0d41d..816091df51b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java
@@ -37,7 +37,6 @@ import org.apache.doris.common.Config;
 import org.apache.doris.common.DdlException;
 import org.apache.doris.common.Pair;
 import org.apache.doris.datasource.CatalogIf;
-import org.apache.doris.datasource.CatalogMgr;
 import org.apache.doris.datasource.ExternalCatalog;
 import org.apache.doris.nereids.types.DataType;
 import org.apache.doris.policy.Policy;
@@ -1679,16 +1678,6 @@ public class PropertyAnalyzer {
         // "access_controller.properties.prop2" = "yyy",
         // )
         // 1. get access controller class
-        String acClass = 
properties.getOrDefault(CatalogMgr.ACCESS_CONTROLLER_CLASS_PROP, "");
-        if (!Strings.isNullOrEmpty(acClass)) {
-            // 2. check if class exists
-            try {
-                Class.forName(acClass);
-            } catch (ClassNotFoundException e) {
-                throw new AnalysisException("failed to find class " + acClass, 
e);
-            }
-        }
-
         if (isAlter) {
             // The 'use_meta_cache' property can not be modified
             if (properties.containsKey(ExternalCatalog.USE_META_CACHE)) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/AuthenticatorManager.java
 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/AuthenticatorManager.java
index 343134fb8fd..8ba711e6655 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/AuthenticatorManager.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/AuthenticatorManager.java
@@ -17,22 +17,21 @@
 
 package org.apache.doris.mysql.authenticate;
 
-import org.apache.doris.common.EnvUtils;
+import org.apache.doris.common.util.ClassLoaderUtils;
 import org.apache.doris.mysql.MysqlAuthPacket;
 import org.apache.doris.mysql.MysqlChannel;
 import org.apache.doris.mysql.MysqlHandshakePacket;
 import org.apache.doris.mysql.MysqlProto;
 import org.apache.doris.mysql.MysqlSerializer;
 import org.apache.doris.mysql.authenticate.password.Password;
+import org.apache.doris.plugin.PropertiesUtils;
 import org.apache.doris.qe.ConnectContext;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
-import java.io.File;
 import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.util.List;
 import java.util.Optional;
 import java.util.Properties;
 import java.util.ServiceLoader;
@@ -66,9 +65,28 @@ public class AuthenticatorManager {
         for (AuthenticatorFactory factory : loader) {
             LOG.info("Found Authenticator Plugin Factory: {}", 
factory.factoryIdentifier());
             if (factory.factoryIdentifier().equalsIgnoreCase(identifier)) {
-                return factory.create(loadConfigFile());
+                Properties properties = 
PropertiesUtils.loadAuthenticationConfigFile();
+                return factory.create(properties);
             }
         }
+        return loadCustomerFactories(identifier);
+
+    }
+
+    private Authenticator loadCustomerFactories(String identifier) throws 
Exception {
+        List<AuthenticatorFactory> factories = 
ClassLoaderUtils.loadServicesFromDirectory(AuthenticatorFactory.class);
+        if (factories.isEmpty()) {
+            LOG.info("No customer authenticator found, using default 
authenticator");
+            return defaultAuthenticator;
+        }
+        for (AuthenticatorFactory factory : factories) {
+            LOG.info("Found Customer Authenticator Plugin Factory: {}", 
factory.factoryIdentifier());
+            if (factory.factoryIdentifier().equalsIgnoreCase(identifier)) {
+                Properties properties = 
PropertiesUtils.loadAuthenticationConfigFile();
+                return factory.create(properties);
+            }
+        }
+
         throw new RuntimeException("No AuthenticatorFactory found for 
identifier: " + identifier);
     }
 
@@ -100,15 +118,4 @@ public class AuthenticatorManager {
     private Authenticator chooseAuthenticator(String userName) {
         return authTypeAuthenticator.canDeal(userName) ? authTypeAuthenticator 
: defaultAuthenticator;
     }
-
-    private static Properties loadConfigFile() throws Exception {
-        String configFilePath = EnvUtils.getDorisHome() + 
"/conf/authenticate.conf";
-        if (new File(configFilePath).exists()) {
-            LOG.info("Loading authenticate configuration file: {}", 
configFilePath);
-            Properties properties = new Properties();
-            properties.load(Files.newInputStream(Paths.get(configFilePath)));
-            return properties;
-        }
-        return new Properties();
-    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerFactory.java
 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerFactory.java
index d4e0400c9eb..8d1481aa070 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerFactory.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerFactory.java
@@ -20,6 +20,14 @@ package org.apache.doris.mysql.privilege;
 import java.util.Map;
 
 public interface AccessControllerFactory {
+    /**
+     * Returns the identifier for the factory, such as "range-doris".
+     *
+     * @return the factory identifier
+     */
+    default String factoryIdentifier() {
+        return this.getClass().getSimpleName();
+    }
 
     CatalogAccessController createAccessController(Map<String, String> prop);
 }
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 86aad9af71e..91f8e415cdb 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
@@ -22,12 +22,13 @@ import org.apache.doris.analysis.TableName;
 import org.apache.doris.analysis.UserIdentity;
 import org.apache.doris.catalog.AuthorizationInfo;
 import org.apache.doris.catalog.Env;
-import 
org.apache.doris.catalog.authorizer.ranger.doris.RangerDorisAccessController;
 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.ExternalCatalog;
 import org.apache.doris.datasource.InternalCatalog;
+import org.apache.doris.plugin.PropertiesUtils;
 import org.apache.doris.qe.ConnectContext;
 
 import com.google.common.base.Preconditions;
@@ -36,11 +37,14 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
+import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.ServiceLoader;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * AccessControllerManager is the entry point of privilege authentication.
@@ -53,19 +57,62 @@ public class AccessControllerManager {
     private static final Logger LOG = 
LogManager.getLogger(AccessControllerManager.class);
 
     private Auth auth;
+    // Default access controller instance used for handling cases where no 
specific controller is specified
     private CatalogAccessController defaultAccessController;
+    // Map that stores the mapping between catalogs and their corresponding 
access controllers
     private Map<String, CatalogAccessController> ctlToCtlAccessController = 
Maps.newConcurrentMap();
+    // Cache of loaded access controller factories for quick creation of new 
access controllers
+    private ConcurrentHashMap<String, AccessControllerFactory> 
accessControllerFactoriesCache
+            = new ConcurrentHashMap<>();
+    // Mapping between access controller class names and their identifiers for 
easy lookup of factory identifiers
+    private ConcurrentHashMap<String, String> accessControllerClassNameMapping 
= new ConcurrentHashMap<>();
 
     public AccessControllerManager(Auth auth) {
         this.auth = auth;
-        if (Config.access_controller_type.equalsIgnoreCase("ranger-doris")) {
-            defaultAccessController = 
RangerDorisAccessController.getInstance("doris");
-        } else {
-            defaultAccessController = new InternalAccessController(auth);
-        }
+        loadAccessControllerPlugins();
+        String accessControllerName = Config.access_controller_type;
+        this.defaultAccessController = 
loadAccessControllerOrThrow(accessControllerName);
         ctlToCtlAccessController.put(InternalCatalog.INTERNAL_CATALOG_NAME, 
defaultAccessController);
     }
 
+    private CatalogAccessController loadAccessControllerOrThrow(String 
accessControllerName) {
+        if (accessControllerName.equalsIgnoreCase("default")) {
+            return new InternalAccessController(auth);
+        }
+        if (accessControllerFactoriesCache.containsKey(accessControllerName)) {
+            Map<String, String> prop;
+            try {
+                prop = PropertiesUtils.loadAccessControllerPropertiesOrNull();
+            } catch (IOException e) {
+                throw new RuntimeException("Failed to load authorization 
properties."
+                        + "Please check the configuration file, authorization 
name is " + accessControllerName, e);
+            }
+            return 
accessControllerFactoriesCache.get(accessControllerName).createAccessController(prop);
+        }
+        throw new RuntimeException("No authorization plugin factory found for 
" + accessControllerName
+                + ". Please confirm that your plugin is placed in the correct 
location.");
+    }
+
+    private void loadAccessControllerPlugins() {
+        ServiceLoader<AccessControllerFactory> loaderFromClasspath = 
ServiceLoader.load(AccessControllerFactory.class);
+        for (AccessControllerFactory factory : loaderFromClasspath) {
+            LOG.info("Found Authentication Plugin Factories: {} from class 
path.", factory.factoryIdentifier());
+            accessControllerFactoriesCache.put(factory.factoryIdentifier(), 
factory);
+            accessControllerClassNameMapping.put(factory.getClass().getName(), 
factory.factoryIdentifier());
+        }
+        List<AccessControllerFactory> loader = null;
+        try {
+            loader = 
ClassLoaderUtils.loadServicesFromDirectory(AccessControllerFactory.class);
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to load Authentication Plugin 
Factories", e);
+        }
+        for (AccessControllerFactory factory : loader) {
+            LOG.info("Found Access Controller Plugin Factory: {} from 
directory.", factory.factoryIdentifier());
+            accessControllerFactoriesCache.put(factory.factoryIdentifier(), 
factory);
+            accessControllerClassNameMapping.put(factory.getClass().getName(), 
factory.factoryIdentifier());
+        }
+    }
+
     public CatalogAccessController getAccessControllerOrDefault(String ctl) {
         CatalogAccessController catalogAccessController = 
ctlToCtlAccessController.get(ctl);
         if (catalogAccessController != null) {
@@ -95,23 +142,28 @@ public class AccessControllerManager {
     }
 
     public void createAccessController(String ctl, String acFactoryClassName, 
Map<String, String> prop,
-            boolean isDryRun) {
-        Class<?> factoryClazz = null;
-        try {
-            factoryClazz = Class.forName(acFactoryClassName);
-            AccessControllerFactory factory = (AccessControllerFactory) 
factoryClazz.newInstance();
-            CatalogAccessController accessController = 
factory.createAccessController(prop);
-            if (!isDryRun) {
-                ctlToCtlAccessController.put(ctl, accessController);
-                LOG.info("create access controller {} for catalog {}", ctl, 
acFactoryClassName);
-            }
-        } catch (ClassNotFoundException e) {
-            throw new RuntimeException(e);
-        } catch (InstantiationException e) {
-            throw new RuntimeException(e);
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
+                                       boolean isDryRun) {
+        String pluginIdentifier = 
getPluginIdentifierForAccessController(acFactoryClassName);
+        CatalogAccessController accessController = 
accessControllerFactoriesCache.get(pluginIdentifier)
+                .createAccessController(prop);
+        if (!isDryRun) {
+            ctlToCtlAccessController.put(ctl, accessController);
+            LOG.info("create access controller {} for catalog {}", 
acFactoryClassName, ctl);
+        }
+    }
+
+    private String getPluginIdentifierForAccessController(String acClassName) {
+        String pluginIdentifier = null;
+        if (accessControllerClassNameMapping.containsKey(acClassName)) {
+            pluginIdentifier = 
accessControllerClassNameMapping.get(acClassName);
+        }
+        if (accessControllerFactoriesCache.containsKey(acClassName)) {
+            pluginIdentifier = acClassName;
+        }
+        if (null == pluginIdentifier || 
!accessControllerFactoriesCache.containsKey(pluginIdentifier)) {
+            throw new RuntimeException("Access Controller Plugin Factory not 
found for " + acClassName);
         }
+        return pluginIdentifier;
     }
 
     public void removeAccessController(String ctl) {
@@ -166,7 +218,7 @@ public class AccessControllerManager {
     }
 
     public boolean checkTblPriv(ConnectContext ctx, String qualifiedCtl,
-            String qualifiedDb, String tbl, PrivPredicate wanted) {
+                                String qualifiedDb, String tbl, PrivPredicate 
wanted) {
         if (ctx.isSkipAuth()) {
             return true;
         }
@@ -181,7 +233,7 @@ public class AccessControllerManager {
     // ==== Column ====
     // If param has ctx, we can skip auth by isSkipAuth field in ctx
     public void checkColumnsPriv(ConnectContext ctx, String ctl, String 
qualifiedDb, String tbl, Set<String> cols,
-            PrivPredicate wanted) throws UserException {
+                                 PrivPredicate wanted) throws UserException {
         if (ctx.isSkipAuth()) {
             return;
         }
@@ -190,7 +242,7 @@ public class AccessControllerManager {
 
     public void checkColumnsPriv(UserIdentity currentUser, String
             ctl, String qualifiedDb, String tbl, Set<String> cols,
-            PrivPredicate wanted) throws UserException {
+                                 PrivPredicate wanted) throws UserException {
         boolean hasGlobal = checkGlobalPriv(currentUser, wanted);
         CatalogAccessController accessController = 
getAccessControllerOrDefault(ctl);
         long start = System.currentTimeMillis();
@@ -217,7 +269,7 @@ public class AccessControllerManager {
     }
 
     public boolean checkCloudPriv(UserIdentity currentUser, String cloudName,
-            PrivPredicate wanted, ResourceTypeEnum type) {
+                                  PrivPredicate wanted, ResourceTypeEnum type) 
{
         return defaultAccessController.checkCloudPriv(currentUser, cloudName, 
wanted, type);
     }
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerFactory.java
 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/RangerDorisAccessControllerFactory.java
similarity index 67%
copy from 
fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerFactory.java
copy to 
fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/RangerDorisAccessControllerFactory.java
index d4e0400c9eb..28093ad7886 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerFactory.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/RangerDorisAccessControllerFactory.java
@@ -17,9 +17,18 @@
 
 package org.apache.doris.mysql.privilege;
 
+import 
org.apache.doris.catalog.authorizer.ranger.doris.RangerDorisAccessController;
+
 import java.util.Map;
 
-public interface AccessControllerFactory {
+public class RangerDorisAccessControllerFactory implements 
AccessControllerFactory {
+    @Override
+    public String factoryIdentifier() {
+        return "ranger-doris";
+    }
 
-    CatalogAccessController createAccessController(Map<String, String> prop);
+    @Override
+    public RangerDorisAccessController createAccessController(Map<String, 
String> prop) {
+        return new RangerDorisAccessController("doris");
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/UserPropertyMgr.java
 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/UserPropertyMgr.java
index 816ce769a31..29ae1f438a1 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/UserPropertyMgr.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/UserPropertyMgr.java
@@ -29,6 +29,7 @@ import org.apache.doris.common.UserException;
 import org.apache.doris.common.io.Text;
 import org.apache.doris.common.io.Writable;
 import org.apache.doris.load.DppConfig;
+import org.apache.doris.mysql.authenticate.AuthenticateType;
 import org.apache.doris.persist.gson.GsonUtils;
 import org.apache.doris.resource.Tag;
 
@@ -55,9 +56,9 @@ public class UserPropertyMgr implements Writable {
     protected Map<String, UserProperty> propertyMap = Maps.newHashMap();
     public static final String ROOT_USER = "root";
     public static final String SYSTEM_RESOURCE_USER = "system";
-    public static final String LDAP_RESOURCE_USER = "ldap";
-
-    private static final UserProperty LDAP_PROPERTY = new 
UserProperty(LDAP_RESOURCE_USER);
+    public static final String DEFAULT_RESOURCE_USER = 
Config.authentication_type;
+    // When using a non-internal authentication plugin, the user property 
information uses the default configuration.
+    private static final UserProperty DEFAULT_USER_PROPERTY = new 
UserProperty(DEFAULT_RESOURCE_USER);
     @SerializedName(value = "resourceVersion")
     private AtomicLong resourceVersion = new AtomicLong(0);
 
@@ -93,7 +94,7 @@ public class UserPropertyMgr implements Writable {
 
     public int getQueryTimeout(String qualifiedUser) {
         UserProperty existProperty = propertyMap.get(qualifiedUser);
-        existProperty = getLdapPropertyIfNull(qualifiedUser, existProperty);
+        existProperty = getPropertyIfNull(qualifiedUser, existProperty);
         if (existProperty == null) {
             return 0;
         }
@@ -102,7 +103,7 @@ public class UserPropertyMgr implements Writable {
 
     public int getInsertTimeout(String qualifiedUser) {
         UserProperty existProperty = propertyMap.get(qualifiedUser);
-        existProperty = getLdapPropertyIfNull(qualifiedUser, existProperty);
+        existProperty = getPropertyIfNull(qualifiedUser, existProperty);
         if (existProperty == null) {
             return 0;
         }
@@ -111,7 +112,7 @@ public class UserPropertyMgr implements Writable {
 
     public long getMaxConn(String qualifiedUser) {
         UserProperty existProperty = propertyMap.get(qualifiedUser);
-        existProperty = getLdapPropertyIfNull(qualifiedUser, existProperty);
+        existProperty = getPropertyIfNull(qualifiedUser, existProperty);
         if (existProperty == null) {
             return 0;
         }
@@ -120,7 +121,7 @@ public class UserPropertyMgr implements Writable {
 
     public long getMaxQueryInstances(String qualifiedUser) {
         UserProperty existProperty = propertyMap.get(qualifiedUser);
-        existProperty = getLdapPropertyIfNull(qualifiedUser, existProperty);
+        existProperty = getPropertyIfNull(qualifiedUser, existProperty);
         if (existProperty == null) {
             return Config.default_max_query_instances;
         }
@@ -139,21 +140,21 @@ public class UserPropertyMgr implements Writable {
         List<String> ret = new ArrayList<>();
         users.forEach(
                 u -> {
-                UserProperty userProperty = propertyMap.get(u);
-                if (userProperty == null) {
-                    return;
-                }
-                if (clusterName.equals(userProperty.getDefaultCloudCluster())) 
{
-                    ret.add(ClusterNamespace.getNameFromFullName(u));
+                    UserProperty userProperty = propertyMap.get(u);
+                    if (userProperty == null) {
+                        return;
+                    }
+                    if 
(clusterName.equals(userProperty.getDefaultCloudCluster())) {
+                        ret.add(ClusterNamespace.getNameFromFullName(u));
+                    }
                 }
-            }
         );
         return ret;
     }
 
     public int getParallelFragmentExecInstanceNum(String qualifiedUser) {
         UserProperty existProperty = propertyMap.get(qualifiedUser);
-        existProperty = getLdapPropertyIfNull(qualifiedUser, existProperty);
+        existProperty = getPropertyIfNull(qualifiedUser, existProperty);
         if (existProperty == null) {
             return -1;
         }
@@ -162,7 +163,7 @@ public class UserPropertyMgr implements Writable {
 
     public Set<Tag> getResourceTags(String qualifiedUser) {
         UserProperty existProperty = propertyMap.get(qualifiedUser);
-        existProperty = getLdapPropertyIfNull(qualifiedUser, existProperty);
+        existProperty = getPropertyIfNull(qualifiedUser, existProperty);
         if (existProperty == null) {
             return UserProperty.INVALID_RESOURCE_TAGS;
         }
@@ -182,7 +183,7 @@ public class UserPropertyMgr implements Writable {
         Pair<String, DppConfig> loadClusterInfo = null;
 
         UserProperty property = propertyMap.get(qualifiedUser);
-        property = getLdapPropertyIfNull(qualifiedUser, property);
+        property = getPropertyIfNull(qualifiedUser, property);
         if (property == null) {
             throw new DdlException("User " + qualifiedUser + " does not 
exist");
         }
@@ -192,7 +193,7 @@ public class UserPropertyMgr implements Writable {
 
     public List<List<String>> fetchUserProperty(String qualifiedUser) throws 
AnalysisException {
         UserProperty property = propertyMap.get(qualifiedUser);
-        property = getLdapPropertyIfNull(qualifiedUser, property);
+        property = getPropertyIfNull(qualifiedUser, property);
         if (property == null) {
             throw new AnalysisException("User " + qualifiedUser + " does not 
exist");
         }
@@ -201,16 +202,16 @@ public class UserPropertyMgr implements Writable {
 
     public String[] getSqlBlockRules(String qualifiedUser) {
         UserProperty existProperty = propertyMap.get(qualifiedUser);
-        existProperty = getLdapPropertyIfNull(qualifiedUser, existProperty);
+        existProperty = getPropertyIfNull(qualifiedUser, existProperty);
         if (existProperty == null) {
-            return new String[] {};
+            return new String[]{};
         }
         return existProperty.getSqlBlockRules();
     }
 
     public int getCpuResourceLimit(String qualifiedUser) {
         UserProperty existProperty = propertyMap.get(qualifiedUser);
-        existProperty = getLdapPropertyIfNull(qualifiedUser, existProperty);
+        existProperty = getPropertyIfNull(qualifiedUser, existProperty);
         if (existProperty == null) {
             return -1;
         }
@@ -219,7 +220,7 @@ public class UserPropertyMgr implements Writable {
 
     public long getExecMemLimit(String qualifiedUser) {
         UserProperty existProperty = propertyMap.get(qualifiedUser);
-        existProperty = getLdapPropertyIfNull(qualifiedUser, existProperty);
+        existProperty = getPropertyIfNull(qualifiedUser, existProperty);
         if (existProperty == null) {
             return -1;
         }
@@ -228,7 +229,7 @@ public class UserPropertyMgr implements Writable {
 
     public String getWorkloadGroup(String qualifiedUser) {
         UserProperty existProperty = propertyMap.get(qualifiedUser);
-        existProperty = getLdapPropertyIfNull(qualifiedUser, existProperty);
+        existProperty = getPropertyIfNull(qualifiedUser, existProperty);
         if (existProperty == null) {
             return null;
         }
@@ -244,9 +245,24 @@ public class UserPropertyMgr implements Writable {
         return Pair.of(false, "");
     }
 
-    private UserProperty getLdapPropertyIfNull(String qualifiedUser, 
UserProperty existProperty) {
-        if (existProperty == null && 
Env.getCurrentEnv().getAuth().getLdapManager().doesUserExist(qualifiedUser)) {
-            return LDAP_PROPERTY;
+    /**
+     * The method determines which user property to return based on the 
existProperty parameter
+     * and system configuration:
+     * If existProperty is not null, return it directly.
+     * If the authentication type is LDAP and the user exists in LDAP, return 
DEFAULT_USER_PROPERTY.
+     * If the authentication type is not the default type, return 
DEFAULT_USER_PROPERTY.
+     * Otherwise, return existProperty.
+     */
+    private UserProperty getPropertyIfNull(String qualifiedUser, UserProperty 
existProperty) {
+        if (null != existProperty) {
+            return existProperty;
+        }
+        if 
(AuthenticateType.LDAP.name().equalsIgnoreCase(Config.authentication_type)
+                && 
Env.getCurrentEnv().getAuth().getLdapManager().doesUserExist(qualifiedUser)) {
+            return DEFAULT_USER_PROPERTY;
+        }
+        if 
(!Config.authentication_type.equalsIgnoreCase(AuthenticateType.DEFAULT.name())) 
{
+            return DEFAULT_USER_PROPERTY;
         }
         return existProperty;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/plugin/PropertiesUtils.java 
b/fe/fe-core/src/main/java/org/apache/doris/plugin/PropertiesUtils.java
new file mode 100644
index 00000000000..7318be7da61
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/plugin/PropertiesUtils.java
@@ -0,0 +1,68 @@
+// 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.plugin;
+
+import org.apache.doris.common.Config;
+import org.apache.doris.common.EnvUtils;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+public class PropertiesUtils {
+
+    public static final Logger LOG = 
LogManager.getLogger(PropertiesUtils.class);
+
+    public static Map<String, String> loadAccessControllerPropertiesOrNull() 
throws IOException {
+        String configFilePath = EnvUtils.getDorisHome() + 
Config.authorization_config_file_path;
+        if (new File(configFilePath).exists()) {
+            Properties properties = new Properties();
+            properties.load(Files.newInputStream(Paths.get(configFilePath)));
+            return propertiesToMap(properties);
+        }
+        return null;
+    }
+
+    public static Properties loadAuthenticationConfigFile() throws Exception {
+        String configFilePath = EnvUtils.getDorisHome() + 
Config.authentication_config_file_path;
+        if (new File(configFilePath).exists()) {
+            LOG.info("Loading authenticate configuration file: {}", 
configFilePath);
+            Properties properties = new Properties();
+            properties.load(Files.newInputStream(Paths.get(configFilePath)));
+            return properties;
+        }
+        return new Properties();
+    }
+
+    public static Map<String, String> propertiesToMap(Properties properties) {
+        Map<String, String> map = new HashMap<>();
+        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
+            String key = String.valueOf(entry.getKey());
+            String value = String.valueOf(entry.getValue());
+            map.put(key, value);
+        }
+        return map;
+    }
+}
diff --git 
a/fe/fe-core/src/main/resources/META-INF/services/org.apache.doris.mysql.privilege.AccessControllerFactory
 
b/fe/fe-core/src/main/resources/META-INF/services/org.apache.doris.mysql.privilege.AccessControllerFactory
new file mode 100644
index 00000000000..e2100cb8b23
--- /dev/null
+++ 
b/fe/fe-core/src/main/resources/META-INF/services/org.apache.doris.mysql.privilege.AccessControllerFactory
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+#
+org.apache.doris.mysql.privilege.RangerDorisAccessControllerFactory
+org.apache.doris.catalog.authorizer.ranger.hive.RangerHiveAccessControllerFactory
\ No newline at end of file
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAccessControllerFactory.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/privileges/CustomAccessControllerFactory.java
similarity index 78%
copy from 
fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAccessControllerFactory.java
copy to 
fe/fe-core/src/test/java/org/apache/doris/nereids/privileges/CustomAccessControllerFactory.java
index 3e9f11d9f8e..f30ab8def4f 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAccessControllerFactory.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/privileges/CustomAccessControllerFactory.java
@@ -15,16 +15,21 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.catalog.authorizer.ranger.hive;
+package org.apache.doris.nereids.privileges;
 
 import org.apache.doris.mysql.privilege.AccessControllerFactory;
 import org.apache.doris.mysql.privilege.CatalogAccessController;
 
 import java.util.Map;
 
-public class RangerHiveAccessControllerFactory implements 
AccessControllerFactory {
+public class CustomAccessControllerFactory implements AccessControllerFactory {
+    @Override
+    public String factoryIdentifier() {
+        return "CustomAccess";
+    }
+
     @Override
     public CatalogAccessController createAccessController(Map<String, String> 
prop) {
-        return new RangerHiveAccessController(prop);
+        return new TestCheckPrivileges.SimpleCatalogAccessController();
     }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/privileges/TestCheckPrivileges.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/privileges/TestCheckPrivileges.java
index dafc33a64af..89220e7dffd 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/privileges/TestCheckPrivileges.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/privileges/TestCheckPrivileges.java
@@ -23,10 +23,10 @@ import org.apache.doris.catalog.Column;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.catalog.PrimitiveType;
 import org.apache.doris.common.AuthorizationException;
+import org.apache.doris.common.DdlException;
 import org.apache.doris.common.FeConstants;
 import org.apache.doris.datasource.CatalogMgr;
 import 
org.apache.doris.datasource.test.TestExternalCatalog.TestCatalogProvider;
-import org.apache.doris.mysql.privilege.AccessControllerFactory;
 import org.apache.doris.mysql.privilege.AccessControllerManager;
 import org.apache.doris.mysql.privilege.CatalogAccessController;
 import org.apache.doris.mysql.privilege.DataMaskPolicy;
@@ -92,10 +92,20 @@ public class TestCheckPrivileges extends TestWithFeService 
implements GeneratedM
         String catalogProvider
                 = 
"org.apache.doris.nereids.privileges.TestCheckPrivileges$CustomCatalogProvider";
         String accessControllerFactory
-                = 
"org.apache.doris.nereids.privileges.TestCheckPrivileges$CustomAccessControllerFactory";
-
+                = 
"org.apache.doris.nereids.privileges.CustomAccessControllerFactory";
         String catalog = "custom_catalog";
         String db = "test_db";
+        String failedAccessControllerFactory
+                = 
"org.apache.doris.nereids.privileges.FailedAccessControllerFactory";
+        //try to create catalog with failed access controller
+        Assertions.assertThrows(DdlException.class, () -> {
+            createCatalog("create catalog " + catalog + " properties("
+                    + " \"type\"=\"test\","
+                    + " \"catalog_provider.class\"=\"" + catalogProvider + 
"\","
+                    + " \"" + CatalogMgr.ACCESS_CONTROLLER_CLASS_PROP + 
"\"=\"" + failedAccessControllerFactory + "\""
+                    + ")");
+        }, "Failed to init access controller");
+
         createCatalog("create catalog " + catalog + " properties("
                 + " \"type\"=\"test\","
                 + " \"catalog_provider.class\"=\"" + catalogProvider + "\","
@@ -314,13 +324,6 @@ public class TestCheckPrivileges extends TestWithFeService 
implements GeneratedM
         }
     }
 
-    public static class CustomAccessControllerFactory implements 
AccessControllerFactory {
-        @Override
-        public CatalogAccessController createAccessController(Map<String, 
String> prop) {
-            return new SimpleCatalogAccessController();
-        }
-    }
-
     public static class SimpleCatalogAccessController implements 
CatalogAccessController {
         private static ThreadLocal<List<TablePrivilege>> tablePrivileges = new 
ThreadLocal<>();
         private static ThreadLocal<List<ColumnPrivilege>> columnPrivileges = 
new ThreadLocal<>();
diff --git 
a/fe/fe-core/src/test/resources/META-INF/services/org.apache.doris.mysql.privilege.AccessControllerFactory
 
b/fe/fe-core/src/test/resources/META-INF/services/org.apache.doris.mysql.privilege.AccessControllerFactory
new file mode 100644
index 00000000000..83924e7e0f6
--- /dev/null
+++ 
b/fe/fe-core/src/test/resources/META-INF/services/org.apache.doris.mysql.privilege.AccessControllerFactory
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+#
+org.apache.doris.nereids.privileges.CustomAccessControllerFactory
\ No newline at end of file


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

Reply via email to