This is an automated email from the ASF dual-hosted git repository. kirs pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new c34e86b1160 [Feat](Authentication)Supporting Authentication Plugins and Kernel Isolation (#41100) c34e86b1160 is described below commit c34e86b11608da2295ff69c9e8deef58c0897aec Author: Calvin Kirs <k...@apache.org> AuthorDate: Wed Sep 25 17:45:30 2024 +0800 [Feat](Authentication)Supporting Authentication Plugins and Kernel Isolation (#41100) ## Proposed changes - Use a Child-First ClassLoader to isolate plugins from the kernel, giving priority to plugin classes to prevent conflicts between the plugin and kernel classes. - Allow users to place plugins in a specified directory, such as auth-lib, to avoid classpath conflicts by default. - Support developers in debugging plugins by allowing them to bring in plugins via Maven, making the debugging process more convenient. --- .../main/java/org/apache/doris/common/Config.java | 27 +++- .../doris/common/util/ChildFirstClassLoader.java | 151 +++++++++++++++++++++ .../apache/doris/common/util/ClassLoaderUtils.java | 122 +++++++++++++++++ .../mysql/authenticate/AuthenticatorManager.java | 39 +++--- .../mysql/privilege/AccessControllerManager.java | 18 ++- .../doris/mysql/privilege/UserPropertyMgr.java | 68 ++++++---- .../org/apache/doris/plugin/PropertiesUtils.java | 19 ++- 7 files changed, 391 insertions(+), 53 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 818ab172b93..59522d27a48 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 @@ -2323,12 +2323,6 @@ public class Config extends ConfigBase { @ConfField public static long ranger_cache_size = 10000; - @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"; - /** * This configuration is used to enable the statistics of query information, which will record * the access status of databases, tables, and columns, and can be used to guide the @@ -3105,4 +3099,25 @@ public class Config extends ConfigBase { @ConfField(mutable = true, description = { "设置为 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"; + } 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..d0ebd265401 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/common/util/ClassLoaderUtils.java @@ -0,0 +1,122 @@ +// 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 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 { + // 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) { + throw new IOException("No JAR files found in the specified directory: " + pluginDir); + } + + 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/mysql/authenticate/AuthenticatorManager.java b/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/AuthenticatorManager.java index 68b6724b6d8..de703b306c6 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/AccessControllerManager.java b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerManager.java index bfdf0a7b095..81fae713e44 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 @@ -24,6 +24,7 @@ import org.apache.doris.catalog.AuthorizationInfo; import org.apache.doris.catalog.Env; 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; @@ -92,9 +93,20 @@ public class AccessControllerManager { } private void loadAccessControllerPlugins() { - ServiceLoader<AccessControllerFactory> loader = ServiceLoader.load(AccessControllerFactory.class); + 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: {}", factory.factoryIdentifier()); + LOG.info("Found Access Controller Plugin Factory: {} from directory.", factory.factoryIdentifier()); accessControllerFactoriesCache.put(factory.factoryIdentifier(), factory); accessControllerClassNameMapping.put(factory.getClass().getName(), factory.factoryIdentifier()); } @@ -215,7 +227,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; } 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 index 953a35787b7..7318be7da61 100644 --- 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 @@ -20,6 +20,9 @@ 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; @@ -29,10 +32,11 @@ import java.util.Map; import java.util.Properties; public class PropertiesUtils { - public static final String ACCESS_PROPERTIES_FILE_DIR = Config.authorization_config_file_path; + + public static final Logger LOG = LogManager.getLogger(PropertiesUtils.class); public static Map<String, String> loadAccessControllerPropertiesOrNull() throws IOException { - String configFilePath = EnvUtils.getDorisHome() + ACCESS_PROPERTIES_FILE_DIR; + 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))); @@ -41,6 +45,17 @@ public class PropertiesUtils { 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()) { --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org