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

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


The following commit(s) were added to 
refs/heads/branch-2.1-authentication-plugin by this push:
     new 428588c7ea2 Branch 2.1 authentication plugin commits (#44156)
428588c7ea2 is described below

commit 428588c7ea246d34ea71c4cc60c8737eb11e338d
Author: Calvin Kirs <guoqi...@selectdb.com>
AuthorDate: Mon Nov 18 14:36:57 2024 +0800

    Branch 2.1 authentication plugin commits (#44156)
---
 .../main/java/org/apache/doris/common/Config.java  |  20 +++
 .../main/java/org/apache/doris/catalog/Env.java    |   2 +-
 .../hive/RangerHiveAccessControllerFactory.java    |   6 +
 .../doris/common/util/ChildFirstClassLoader.java   | 151 +++++++++++++++++++++
 .../apache/doris/common/util/ClassLoaderUtils.java | 126 +++++++++++++++++
 .../apache/doris/common/util/PropertyAnalyzer.java |  11 --
 .../doris/mysql/authenticate/AuthenticateType.java |  18 +++
 .../AuthenticatorFactory.java}                     |  20 ++-
 .../mysql/authenticate/AuthenticatorManager.java   |  76 ++++++++---
 .../DefaultAuthenticatorFactory.java}              |  15 +-
 .../mysql/authenticate/ldap/LdapAuthenticator.java |   9 +-
 .../ldap/LdapAuthenticatorFactory.java}            |  20 ++-
 .../mysql/privilege/AccessControllerFactory.java   |   8 ++
 .../mysql/privilege/AccessControllerManager.java   | 100 ++++++++++----
 ...ava => RangerDorisAccessControllerFactory.java} |  13 +-
 .../org/apache/doris/plugin/PropertiesUtils.java   |  68 ++++++++++
 ...e.doris.mysql.authenticate.AuthenticatorFactory |  19 +++
 ...e.doris.mysql.privilege.AccessControllerFactory |  19 +++
 .../privileges/CustomAccessControllerFactory.java} |  11 +-
 .../nereids/privileges/TestCheckPrivileges.java    |  23 ++--
 ...e.doris.mysql.privilege.AccessControllerFactory |  18 +++
 21 files changed, 667 insertions(+), 86 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 59daae8f8d3..08a207eeae8 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
@@ -2878,6 +2878,26 @@ public class Config extends ConfigBase {
             "设置为 true,如果查询无法选择到健康副本时,会打印出该tablet所有副本的详细信息,"})
     public static boolean sql_block_rule_ignore_admin = false;
 
+    @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";
+
     @ConfField(description = {"用于测试,强制将所有的查询forward到master以验证forward query的行为",
             "For testing purposes, all queries are forcibly forwarded to the 
master to verify"
                     + "the behavior of forwarding queries."})
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
index 03679d64330..1d4ea604bfc 100755
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
@@ -729,7 +729,7 @@ public class Env {
 
         this.auth = new Auth();
         this.accessManager = new AccessControllerManager(auth);
-        this.authenticatorManager = new 
AuthenticatorManager(AuthenticateType.getAuthTypeConfig());
+        this.authenticatorManager = new 
AuthenticatorManager(AuthenticateType.getAuthTypeConfigString());
         this.domainResolver = new DomainResolver(auth);
 
         this.metaContext = new MetaContext();
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 545e7a26836..33e3f4a64c1 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 RangerCacheHiveAccessController(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 553b322076a..da163cc6fcf 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
@@ -34,7 +34,6 @@ import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.Config;
 import org.apache.doris.common.DdlException;
 import org.apache.doris.datasource.CatalogIf;
-import org.apache.doris.datasource.CatalogMgr;
 import org.apache.doris.datasource.ExternalCatalog;
 import org.apache.doris.policy.Policy;
 import org.apache.doris.policy.StoragePolicy;
@@ -1400,16 +1399,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/AuthenticateType.java
 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/AuthenticateType.java
index 4281c19bba6..1f16c1f541b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/AuthenticateType.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/AuthenticateType.java
@@ -40,4 +40,22 @@ public enum AuthenticateType {
                 return DEFAULT;
         }
     }
+
+    public static String getAuthTypeConfigString() {
+        String authType = Config.authentication_type.toLowerCase();
+
+        if (LdapConfig.ldap_authentication_enabled) {
+            return LDAP.name();
+        }
+
+        switch (authType) {
+            case "default":
+                return DEFAULT.toString();
+            case "ldap":
+                return LDAP.toString();
+            default:
+                return authType;
+        }
+    }
+
 }
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/authenticate/AuthenticatorFactory.java
similarity index 64%
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/authenticate/AuthenticatorFactory.java
index d4e0400c9eb..25ac87de4e7 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/authenticate/AuthenticatorFactory.java
@@ -15,11 +15,23 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.mysql.privilege;
+package org.apache.doris.mysql.authenticate;
 
-import java.util.Map;
+import java.util.Properties;
 
-public interface AccessControllerFactory {
+public interface AuthenticatorFactory {
+    /**
+     * Creates a new instance of Authenticator.
+     *
+     * @return an instance of Authenticator
+     */
+    Authenticator create(Properties initProps);
 
-    CatalogAccessController createAccessController(Map<String, String> prop);
+    /**
+     * Returns the identifier for the factory, such as "ldap" or "default".
+     *
+     * @return the factory identifier
+     */
+    String factoryIdentifier();
 }
+
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 c00828f82fa..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,47 +17,85 @@
 
 package org.apache.doris.mysql.authenticate;
 
+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.ldap.LdapAuthenticator;
 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.IOException;
+import java.util.List;
 import java.util.Optional;
+import java.util.Properties;
+import java.util.ServiceLoader;
 
 public class AuthenticatorManager {
     private static final Logger LOG = 
LogManager.getLogger(AuthenticatorManager.class);
 
-    private Authenticator defaultAuthenticator;
-    private Authenticator authTypeAuthenticator;
+    private static volatile Authenticator defaultAuthenticator = null;
+    private static volatile Authenticator authTypeAuthenticator = null;
 
-    public AuthenticatorManager(AuthenticateType type) {
-        LOG.info("authenticate type: {}", type);
-        this.defaultAuthenticator = new DefaultAuthenticator();
-        switch (type) {
-            case LDAP:
-                this.authTypeAuthenticator = new LdapAuthenticator();
-                break;
-            case DEFAULT:
-            default:
-                this.authTypeAuthenticator = defaultAuthenticator;
-                break;
+    public AuthenticatorManager(String type) {
+        LOG.info("Authenticate type: {}", type);
+        defaultAuthenticator = new DefaultAuthenticator();
+        if (authTypeAuthenticator == null) {
+            synchronized (AuthenticatorManager.class) {
+                if (authTypeAuthenticator == null) {
+                    try {
+                        authTypeAuthenticator = loadFactoriesByName(type);
+                    } catch (Exception e) {
+                        LOG.warn("Failed to load authenticator by name: {}, 
using default authenticator", type, e);
+                        authTypeAuthenticator = defaultAuthenticator;
+                    }
+                }
+            }
         }
     }
 
+
+    private Authenticator loadFactoriesByName(String identifier) throws 
Exception {
+        ServiceLoader<AuthenticatorFactory> loader = 
ServiceLoader.load(AuthenticatorFactory.class);
+        for (AuthenticatorFactory factory : loader) {
+            LOG.info("Found Authenticator Plugin Factory: {}", 
factory.factoryIdentifier());
+            if (factory.factoryIdentifier().equalsIgnoreCase(identifier)) {
+                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);
+    }
+
     public boolean authenticate(ConnectContext context,
-            String userName,
-            MysqlChannel channel,
-            MysqlSerializer serializer,
-            MysqlAuthPacket authPacket,
-            MysqlHandshakePacket handshakePacket) throws IOException {
+                                String userName,
+                                MysqlChannel channel,
+                                MysqlSerializer serializer,
+                                MysqlAuthPacket authPacket,
+                                MysqlHandshakePacket handshakePacket) throws 
IOException {
         Authenticator authenticator = chooseAuthenticator(userName);
         Optional<Password> password = authenticator.getPasswordResolver()
                 .resolvePassword(context, channel, serializer, authPacket, 
handshakePacket);
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/authenticate/DefaultAuthenticatorFactory.java
similarity index 69%
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/authenticate/DefaultAuthenticatorFactory.java
index d4e0400c9eb..5d073a8296a 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/authenticate/DefaultAuthenticatorFactory.java
@@ -15,11 +15,18 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.mysql.privilege;
+package org.apache.doris.mysql.authenticate;
 
-import java.util.Map;
+import java.util.Properties;
 
-public interface AccessControllerFactory {
+public class DefaultAuthenticatorFactory implements AuthenticatorFactory {
+    @Override
+    public DefaultAuthenticator create(Properties initProps) {
+        return new DefaultAuthenticator();
+    }
 
-    CatalogAccessController createAccessController(Map<String, String> prop);
+    @Override
+    public String factoryIdentifier() {
+        return "default";
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapAuthenticator.java
 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapAuthenticator.java
index e37112372ce..cd9cef469d2 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapAuthenticator.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapAuthenticator.java
@@ -75,10 +75,11 @@ public class LdapAuthenticator implements Authenticator {
         if (qualifiedUser.equals(Auth.ROOT_USER) || 
qualifiedUser.equals(Auth.ADMIN_USER)) {
             return false;
         }
-        if 
(!Env.getCurrentEnv().getAuth().getLdapManager().doesUserExist(qualifiedUser)) {
-            return false;
-        }
-        return true;
+        // Fixme Note: LdapManager should be managed internally within the 
Ldap plugin
+        // and not be placed inside the Env class. This ensures that 
Ldap-related
+        // logic and dependencies are encapsulated within the plugin, promoting
+        // better modularity and maintainability.
+        return 
Env.getCurrentEnv().getAuth().getLdapManager().doesUserExist(qualifiedUser);
     }
 
     /**
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/authenticate/ldap/LdapAuthenticatorFactory.java
similarity index 65%
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/authenticate/ldap/LdapAuthenticatorFactory.java
index d4e0400c9eb..fba5c350d39 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/authenticate/ldap/LdapAuthenticatorFactory.java
@@ -15,11 +15,23 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.mysql.privilege;
+package org.apache.doris.mysql.authenticate.ldap;
 
-import java.util.Map;
+import org.apache.doris.mysql.authenticate.AuthenticatorFactory;
 
-public interface AccessControllerFactory {
+import java.util.Properties;
+
+public class LdapAuthenticatorFactory implements AuthenticatorFactory {
+
+
+    @Override
+    public LdapAuthenticator create(Properties initProps) {
+        return new LdapAuthenticator();
+    }
+
+    @Override
+    public String factoryIdentifier() {
+        return "ldap";
+    }
 
-    CatalogAccessController createAccessController(Map<String, String> prop);
 }
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 42fa769d033..67d98837720 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
@@ -21,12 +21,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.RangerCacheDorisAccessController;
 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;
@@ -34,11 +35,14 @@ import com.google.common.collect.Maps;
 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.
@@ -51,19 +55,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 = new 
RangerCacheDorisAccessController("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) {
@@ -93,23 +140,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) {
@@ -159,7 +211,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;
         }
@@ -183,7 +235,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);
         accessController.checkColsPriv(hasGlobal, currentUser, ctl, 
qualifiedDb,
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 66%
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..297fe5c708c 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.RangerCacheDorisAccessController;
+
 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 RangerCacheDorisAccessController createAccessController(Map<String, 
String> prop) {
+        return new RangerCacheDorisAccessController("doris");
+    }
 }
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.authenticate.AuthenticatorFactory
 
b/fe/fe-core/src/main/resources/META-INF/services/org.apache.doris.mysql.authenticate.AuthenticatorFactory
new file mode 100644
index 00000000000..3a013ff7f32
--- /dev/null
+++ 
b/fe/fe-core/src/main/resources/META-INF/services/org.apache.doris.mysql.authenticate.AuthenticatorFactory
@@ -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.authenticate.DefaultAuthenticatorFactory
+org.apache.doris.mysql.authenticate.ldap.LdapAuthenticatorFactory
\ No newline at end of file
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 545e7a26836..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 RangerCacheHiveAccessController(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 07cbb002a64..24c2e656dca 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
@@ -22,10 +22,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;
@@ -91,10 +91,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 + "\","
@@ -313,13 +323,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: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to