This is an automated email from the ASF dual-hosted git repository.
morrySnow 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 6e900dd2310 [improvement](fe) Support LDAP default roles (#63411)
6e900dd2310 is described below
commit 6e900dd2310b2f726b66bc7d6d0e63f572dd3e9d
Author: Raiden <[email protected]>
AuthorDate: Tue Jun 2 11:32:14 2026 +0800
[improvement](fe) Support LDAP default roles (#63411)
### What problem does this PR solve?
Problem Summary:
LDAP temporary users could only receive roles mapped from LDAP groups
and the built-in information_schema-only role. This PR adds
`ldap_default_roles` so every LDAP-authenticated user can receive
configured Doris roles while still keeping LDAP group roles.
### Release note
Support configuring default Doris roles for LDAP-authenticated users
through `ldap_default_roles`.
---
conf/ldap.conf | 3 +
.../java/org/apache/doris/common/LdapConfig.java | 6 ++
.../main/java/org/apache/doris/catalog/Env.java | 3 +
.../doris/mysql/authenticate/ldap/LdapManager.java | 30 ++++--
.../java/org/apache/doris/catalog/EnvTest.java | 37 +++++++
.../mysql/authenticate/ldap/LdapManagerTest.java | 111 ++++++++++++++++++++-
6 files changed, 183 insertions(+), 7 deletions(-)
diff --git a/conf/ldap.conf b/conf/ldap.conf
index 9388ae7ee50..00647819273 100644
--- a/conf/ldap.conf
+++ b/conf/ldap.conf
@@ -31,6 +31,8 @@
# ldap_user_filter - User lookup filter, the placeholder {login} will be
replaced by the user supplied login.
# ldap_group_basedn - Search base for groups.
# ldap_group_filter - Group lookup filter, the placeholder {login} will be
replaced by the user supplied login. example : "(&(memberUid={login}))"
+# ldap_default_roles - Comma-separated Doris roles granted to every
LDAP-authenticated user.
+# Online updates of ldap_default_roles refresh the LDAP user cache
automatically.
## step2: Restart fe, and use root or admin account to log in to doris.
## step3: Execute sql statement to set ldap admin password:
# set ldap_admin_password = 'password';
@@ -41,6 +43,7 @@ ldap_admin_name = cn=admin,dc=domain,dc=com
ldap_user_basedn = ou=people,dc=domain,dc=com
ldap_user_filter = (&(uid={login}))
ldap_group_basedn = ou=group,dc=domain,dc=com
+# ldap_default_roles = ldap_default_role
# ldap_user_cache_timeout_s = 5 * 60;
diff --git a/fe/fe-common/src/main/java/org/apache/doris/common/LdapConfig.java
b/fe/fe-common/src/main/java/org/apache/doris/common/LdapConfig.java
index be25b5af0a4..82966af525b 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/common/LdapConfig.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/common/LdapConfig.java
@@ -72,6 +72,12 @@ public class LdapConfig extends ConfigBase {
@ConfigBase.ConfField
public static String ldap_group_filter = "";
+ /**
+ * Default Doris roles granted to every LDAP-authenticated user.
+ */
+ @ConfigBase.ConfField(mutable = true)
+ public static String[] ldap_default_roles = {};
+
/**
* The user LDAP information cache time.
* After timeout, the user information will be retrieved from the LDAP
service again.
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 53b6a67cdb5..b13666a2a74 100644
--- 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
@@ -6790,6 +6790,9 @@ public class Env {
*/
public void setMutableConfigWithCallback(String key, String value) throws
ConfigException {
ConfigBase.setMutableConfig(key, value);
+ if ("ldap_default_roles".equals(key)) {
+ getAuth().getLdapManager().refresh(true, null);
+ }
if (configtoThreads.get(key) != null) {
try {
// not atomic. maybe delay to aware. but acceptable.
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapManager.java
b/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapManager.java
index f279647f2a5..2e1e1a26ecb 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapManager.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapManager.java
@@ -36,6 +36,7 @@ import com.google.common.collect.Sets;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -256,12 +257,8 @@ public class LdapManager {
// get user ldap group. the ldap group name should be the same as the
doris role name
List<String> ldapGroups = ldapClient.getGroups(userName);
Set<Role> roles = Sets.newHashSet();
- for (String group : ldapGroups) {
- String qualifiedRole = group;
- if (Env.getCurrentEnv().getAuth().doesRoleExist(qualifiedRole)) {
-
roles.add(Env.getCurrentEnv().getAuth().getRoleByName(qualifiedRole));
- }
- }
+ addExistingRoles(roles, ldapGroups, false);
+ addExistingRoles(roles, Arrays.asList(LdapConfig.ldap_default_roles),
true);
if (LOG.isDebugEnabled()) {
LOG.debug("get user:{} ldap groups:{} and doris roles:{}",
userName, ldapGroups, roles);
}
@@ -272,6 +269,27 @@ public class LdapManager {
return roles;
}
+ private void addExistingRoles(Set<Role> roles, Iterable<String> roleNames,
boolean warnIfMissing) {
+ Auth auth = null;
+ for (String roleName : roleNames) {
+ if (Strings.isNullOrEmpty(roleName)) {
+ continue;
+ }
+ String qualifiedRole = roleName.trim();
+ if (Strings.isNullOrEmpty(qualifiedRole)) {
+ continue;
+ }
+ if (auth == null) {
+ auth = Env.getCurrentEnv().getAuth();
+ }
+ if (auth.doesRoleExist(qualifiedRole)) {
+ roles.add(auth.getRoleByName(qualifiedRole));
+ } else if (warnIfMissing) {
+ LOG.warn("LDAP default role {} does not exist in Doris, ignore
it.", qualifiedRole);
+ }
+ }
+ }
+
public void refresh(boolean isAll, String fullName) {
writeLock();
try {
diff --git a/fe/fe-core/src/test/java/org/apache/doris/catalog/EnvTest.java
b/fe/fe-core/src/test/java/org/apache/doris/catalog/EnvTest.java
index 69a1898a9f1..fe3f4aebadd 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/catalog/EnvTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/EnvTest.java
@@ -17,9 +17,13 @@
package org.apache.doris.catalog;
+import org.apache.doris.common.ConfigBase;
import org.apache.doris.common.FeConstants;
+import org.apache.doris.common.LdapConfig;
import org.apache.doris.common.io.CountingDataOutputStream;
import org.apache.doris.meta.MetaContext;
+import org.apache.doris.mysql.authenticate.ldap.LdapManager;
+import org.apache.doris.mysql.privilege.Auth;
import org.apache.doris.persist.meta.MetaHeader;
import org.junit.After;
@@ -36,6 +40,9 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Random;
public class EnvTest {
@@ -146,4 +153,34 @@ public class EnvTest {
deleteDir(dir);
}
+
+ @Test
+ public void testSetLdapDefaultRolesConfigRefreshesLdapCache() throws
Exception {
+ Env env = Mockito.spy(new Env(false));
+ Auth auth = Mockito.mock(Auth.class);
+ LdapManager ldapManager = Mockito.mock(LdapManager.class);
+ Mockito.doReturn(auth).when(env).getAuth();
+ Mockito.when(auth.getLdapManager()).thenReturn(ldapManager);
+
+ Map<String, Field> oldConfFields = ConfigBase.confFields;
+ Field oldLdapDefaultRolesField =
ConfigBase.ldapConfFields.put("ldap_default_roles",
+ LdapConfig.class.getField("ldap_default_roles"));
+ String[] oldLdapDefaultRoles = LdapConfig.ldap_default_roles;
+ try {
+ ConfigBase.confFields = new HashMap<>();
+
+ env.setMutableConfigWithCallback("ldap_default_roles",
"role1,role2");
+
+ Assert.assertArrayEquals(new String[] {"role1", "role2"},
LdapConfig.ldap_default_roles);
+ Mockito.verify(ldapManager).refresh(true, null);
+ } finally {
+ ConfigBase.confFields = oldConfFields;
+ if (oldLdapDefaultRolesField == null) {
+ ConfigBase.ldapConfFields.remove("ldap_default_roles");
+ } else {
+ ConfigBase.ldapConfFields.put("ldap_default_roles",
oldLdapDefaultRolesField);
+ }
+ LdapConfig.ldap_default_roles = oldLdapDefaultRoles;
+ }
+ }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/mysql/authenticate/ldap/LdapManagerTest.java
b/fe/fe-core/src/test/java/org/apache/doris/mysql/authenticate/ldap/LdapManagerTest.java
index c3c08e606bd..64fffd2c71d 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/mysql/authenticate/ldap/LdapManagerTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/mysql/authenticate/ldap/LdapManagerTest.java
@@ -17,32 +17,65 @@
package org.apache.doris.mysql.authenticate.ldap;
+import org.apache.doris.catalog.Env;
import org.apache.doris.common.Config;
+import org.apache.doris.common.LdapConfig;
import org.apache.doris.common.jmockit.Deencapsulation;
+import org.apache.doris.mysql.privilege.Auth;
+import org.apache.doris.mysql.privilege.Role;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.MockedStatic;
import org.mockito.Mockito;
import java.util.ArrayList;
+import java.util.Arrays;
public class LdapManagerTest {
private static final String USER1 = "user1";
private static final String USER2 = "user2";
+ private static final String LDAP_GROUP_ROLE = "ldap_group_role";
+ private static final String LDAP_DEFAULT_ROLE = "ldap_default_role";
+ private static final String MISSING_LDAP_DEFAULT_ROLE =
"missing_ldap_default_role";
private LdapClient ldapClient = Mockito.mock(LdapClient.class);
@Before
public void setUp() {
Config.authentication_type = "ldap";
+ LdapConfig.ldap_default_roles = new String[0];
}
private void mockClient(boolean userExist, boolean passwd) {
+ mockClient(userExist, passwd, new ArrayList<>());
+ }
+
+ private void mockClient(boolean userExist, boolean passwd,
ArrayList<String> groups) {
Mockito.when(ldapClient.doesUserExist(Mockito.anyString())).thenReturn(userExist);
Mockito.when(ldapClient.checkPassword(Mockito.anyString(),
Mockito.anyString())).thenReturn(passwd);
- Mockito.when(ldapClient.getGroups(Mockito.anyString())).thenReturn(new
ArrayList<>());
+
Mockito.when(ldapClient.getGroups(Mockito.anyString())).thenReturn(groups);
+ }
+
+ private void mockAuth(MockedStatic<Env> envMockedStatic, Role
ldapGroupRole, Role ldapDefaultRole) {
+ mockAuth(envMockedStatic, ldapGroupRole, ldapDefaultRole, true);
+ }
+
+ private void mockAuth(MockedStatic<Env> envMockedStatic, Role
ldapGroupRole, Role ldapDefaultRole,
+ boolean ldapGroupRoleExists) {
+ Env env = Mockito.mock(Env.class);
+ Auth auth = Mockito.mock(Auth.class);
+ envMockedStatic.when(Env::getCurrentEnv).thenReturn(env);
+ Mockito.when(env.getAuth()).thenReturn(auth);
+
Mockito.when(auth.doesRoleExist(LDAP_GROUP_ROLE)).thenReturn(ldapGroupRoleExists);
+ if (ldapGroupRoleExists) {
+
Mockito.when(auth.getRoleByName(LDAP_GROUP_ROLE)).thenReturn(ldapGroupRole);
+ }
+ Mockito.when(auth.doesRoleExist(LDAP_DEFAULT_ROLE)).thenReturn(true);
+
Mockito.when(auth.getRoleByName(LDAP_DEFAULT_ROLE)).thenReturn(ldapDefaultRole);
+
Mockito.when(auth.doesRoleExist(MISSING_LDAP_DEFAULT_ROLE)).thenReturn(false);
}
@Test
@@ -74,4 +107,80 @@ public class LdapManagerTest {
mockClient(true, false);
Assert.assertFalse(ldapManager.checkUserPasswd(USER2, "123"));
}
+
+ @Test
+ public void testGetUserInfoWithLdapDefaultRolesWithoutLdapGroups() {
+ LdapManager ldapManager = new LdapManager();
+ Deencapsulation.setField(ldapManager, "ldapClient", ldapClient);
+ LdapConfig.ldap_default_roles = new String[] {LDAP_DEFAULT_ROLE,
MISSING_LDAP_DEFAULT_ROLE};
+ Role ldapGroupRole = new Role(LDAP_GROUP_ROLE);
+ Role ldapDefaultRole = new Role(LDAP_DEFAULT_ROLE);
+ mockClient(true, true, new ArrayList<>());
+ try (MockedStatic<Env> envMockedStatic =
Mockito.mockStatic(Env.class)) {
+ mockAuth(envMockedStatic, ldapGroupRole, ldapDefaultRole);
+
+ LdapUserInfo ldapUserInfo = ldapManager.getUserInfo(USER1);
+ Assert.assertNotNull(ldapUserInfo);
+
Assert.assertFalse(ldapUserInfo.getRoles().contains(ldapGroupRole));
+
Assert.assertTrue(ldapUserInfo.getRoles().contains(ldapDefaultRole));
+ Assert.assertEquals(2, ldapUserInfo.getRoles().size());
+ }
+ }
+
+ @Test
+ public void testGetUserInfoWithLdapDefaultRolesWhenLdapGroupRoleMissing() {
+ LdapManager ldapManager = new LdapManager();
+ Deencapsulation.setField(ldapManager, "ldapClient", ldapClient);
+ LdapConfig.ldap_default_roles = new String[] {LDAP_DEFAULT_ROLE,
MISSING_LDAP_DEFAULT_ROLE};
+ Role ldapGroupRole = new Role(LDAP_GROUP_ROLE);
+ Role ldapDefaultRole = new Role(LDAP_DEFAULT_ROLE);
+ mockClient(true, true, new
ArrayList<>(Arrays.asList(LDAP_GROUP_ROLE)));
+ try (MockedStatic<Env> envMockedStatic =
Mockito.mockStatic(Env.class)) {
+ mockAuth(envMockedStatic, ldapGroupRole, ldapDefaultRole, false);
+
+ LdapUserInfo ldapUserInfo = ldapManager.getUserInfo(USER1);
+ Assert.assertNotNull(ldapUserInfo);
+
Assert.assertFalse(ldapUserInfo.getRoles().contains(ldapGroupRole));
+
Assert.assertTrue(ldapUserInfo.getRoles().contains(ldapDefaultRole));
+ Assert.assertEquals(2, ldapUserInfo.getRoles().size());
+ }
+ }
+
+ @Test
+ public void testGetUserInfoWithBlankLdapDefaultRoles() {
+ LdapManager ldapManager = new LdapManager();
+ Deencapsulation.setField(ldapManager, "ldapClient", ldapClient);
+ LdapConfig.ldap_default_roles = new String[] {null, "", " ",
LDAP_DEFAULT_ROLE};
+ Role ldapGroupRole = new Role(LDAP_GROUP_ROLE);
+ Role ldapDefaultRole = new Role(LDAP_DEFAULT_ROLE);
+ mockClient(true, true, new
ArrayList<>(Arrays.asList(LDAP_GROUP_ROLE)));
+ try (MockedStatic<Env> envMockedStatic =
Mockito.mockStatic(Env.class)) {
+ mockAuth(envMockedStatic, ldapGroupRole, ldapDefaultRole);
+
+ LdapUserInfo ldapUserInfo = ldapManager.getUserInfo(USER1);
+ Assert.assertNotNull(ldapUserInfo);
+ Assert.assertTrue(ldapUserInfo.getRoles().contains(ldapGroupRole));
+
Assert.assertTrue(ldapUserInfo.getRoles().contains(ldapDefaultRole));
+ Assert.assertEquals(3, ldapUserInfo.getRoles().size());
+ }
+ }
+
+ @Test
+ public void testGetUserInfoWithLdapDefaultRoles() {
+ LdapManager ldapManager = new LdapManager();
+ Deencapsulation.setField(ldapManager, "ldapClient", ldapClient);
+ LdapConfig.ldap_default_roles = new String[] {LDAP_DEFAULT_ROLE,
MISSING_LDAP_DEFAULT_ROLE};
+ Role ldapGroupRole = new Role(LDAP_GROUP_ROLE);
+ Role ldapDefaultRole = new Role(LDAP_DEFAULT_ROLE);
+ mockClient(true, true, new
ArrayList<>(Arrays.asList(LDAP_GROUP_ROLE)));
+ try (MockedStatic<Env> envMockedStatic =
Mockito.mockStatic(Env.class)) {
+ mockAuth(envMockedStatic, ldapGroupRole, ldapDefaultRole);
+
+ LdapUserInfo ldapUserInfo = ldapManager.getUserInfo(USER1);
+ Assert.assertNotNull(ldapUserInfo);
+ Assert.assertTrue(ldapUserInfo.getRoles().contains(ldapGroupRole));
+
Assert.assertTrue(ldapUserInfo.getRoles().contains(ldapDefaultRole));
+ Assert.assertEquals(3, ldapUserInfo.getRoles().size());
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]