diff --git a/conf/ldap.conf b/conf/ldap.conf index 9388ae7ee50b1e..00647819273ce1 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 be25b5af0a4761..82966af525b0d4 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 53b6a67cdb57e1..b13666a2a744f8 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 void replayDropGlobalFunction(FunctionSearchDesc functionSearchDesc) { */ 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 f279647f2a5134..2e1e1a26ecbb2b 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 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 @@ private Set getLdapGroupsRoles(String userName) throws DdlException { // get user ldap group. the ldap group name should be the same as the doris role name List ldapGroups = ldapClient.getGroups(userName); Set 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 @@ private Set getLdapGroupsRoles(String userName) throws DdlException { return roles; } + private void addExistingRoles(Set roles, Iterable 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/mysql/authenticate/ldap/LdapManagerTest.java b/fe/fe-core/src/test/java/org/apache/doris/mysql/authenticate/ldap/LdapManagerTest.java index c3c08e606bda8d..c9bd974f21837f 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,58 @@ 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 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 envMockedStatic, Role ldapGroupRole, Role ldapDefaultRole) { + 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(true); + 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 +100,23 @@ public void testCheckUserPasswd() { mockClient(true, false); Assert.assertFalse(ldapManager.checkUserPasswd(USER2, "123")); } + + @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 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()); + } + } }