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

starocean999 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 c43fc487d5b [fix](auth)fix some ldap bug (#58541)
c43fc487d5b is described below

commit c43fc487d5b2b96afdbdfab635fbef797c2f61c9
Author: zhangdong <[email protected]>
AuthorDate: Wed Dec 17 18:49:09 2025 +0800

    [fix](auth)fix some ldap bug (#58541)
    
    
    fix:
    - roles in ldapUser may be null
    - not support `refresh ldap`
    - user not have priv when `refresh ldap selfName`
    - unclear error message
    - `show grants for ldapUser` not display roles which grant in doris
---
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |  2 +-
 .../doris/mysql/authenticate/ldap/LdapClient.java  | 16 ++++---
 .../mysql/authenticate/ldap/LdapUserInfo.java      |  3 +-
 .../org/apache/doris/mysql/privilege/Auth.java     | 16 ++++++-
 .../plans/commands/refresh/RefreshLdapCommand.java | 10 ++--
 .../mysql/authenticate/ldap/LdapUserInfoTest.java  | 30 ++++++++++++
 .../org/apache/doris/mysql/privilege/AuthTest.java | 25 ++++++++--
 .../suites/auth_p0/test_refresh_ldap_auth.groovy   | 56 ++++++++++++++++++++++
 8 files changed, 142 insertions(+), 16 deletions(-)

diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index fee1619a7d9..3af3f4353dc 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -572,7 +572,7 @@ supportedRefreshStatement
     | REFRESH DATABASE name=multipartIdentifier propertyClause?                
     #refreshDatabase
     | REFRESH TABLE name=multipartIdentifier                                   
     #refreshTable
     | REFRESH DICTIONARY name=multipartIdentifier                              
     #refreshDictionary
-    | REFRESH LDAP (ALL | (FOR user=identifierOrText))                         
     #refreshLdap
+    | REFRESH LDAP (ALL | (FOR user=identifierOrText))?                        
     #refreshLdap
     ;
 
 supportedCleanStatement
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapClient.java
 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapClient.java
index 0d9bbe0c343..d5641ac6c09 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapClient.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapClient.java
@@ -113,9 +113,9 @@ public class LdapClient {
     private void init() {
         LdapInfo ldapInfo = Env.getCurrentEnv().getAuth().getLdapInfo();
         if (ldapInfo == null || !ldapInfo.isValid()) {
-            LOG.error("LDAP info is null or invalid, LDAP admin password may 
not be set");
+            LOG.error("LDAP configuration is incorrect or LDAP admin password 
is not set.");
             ErrorReport.report(ErrorCode.ERROR_LDAP_CONFIGURATION_ERR);
-            throw new RuntimeException("ldapTemplate is not initialized");
+            throw new RuntimeException("LDAP configuration is incorrect or 
LDAP admin password is not set.");
         }
 
         String ldapPassword = 
SymmetricEncryption.decrypt(ldapInfo.getLdapPasswdEncrypted(),
@@ -199,10 +199,11 @@ public class LdapClient {
             return null;
         }
         if (userDns.size() > 1) {
-            LOG.error("{} not unique in LDAP server:{}",
+            String msg = String.format("[%s] not unique in LDAP server: [%s]",
                     getUserFilter(LdapConfig.ldap_user_filter, userName), 
userDns);
+            LOG.error(msg);
             ErrorReport.report(ErrorCode.ERROR_LDAP_USER_NOT_UNIQUE_ERR, 
userName);
-            throw new RuntimeException("User is not unique");
+            throw new RuntimeException(msg);
         }
         return userDns.get(0);
     }
@@ -218,9 +219,12 @@ public class LdapClient {
                         }
                     });
         } catch (Exception e) {
-            LOG.error("Get user dn fail.", e);
+            String msg
+                    = "Failed to retrieve the user's Distinguished Name (DN),"
+                    + "This may be due to incorrect LDAP configuration or an 
unset/incorrect LDAP admin password.";
+            LOG.error(msg, e);
             ErrorReport.report(ErrorCode.ERROR_LDAP_CONFIGURATION_ERR);
-            throw e;
+            throw new RuntimeException(msg);
         }
     }
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapUserInfo.java
 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapUserInfo.java
index b607c2c8a00..d0f3481bf4f 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapUserInfo.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/authenticate/ldap/LdapUserInfo.java
@@ -20,6 +20,7 @@ package org.apache.doris.mysql.authenticate.ldap;
 import org.apache.doris.common.LdapConfig;
 import org.apache.doris.mysql.privilege.Role;
 
+import java.util.Collections;
 import java.util.Objects;
 import java.util.Set;
 
@@ -50,7 +51,7 @@ public class LdapUserInfo {
         this.isExists = false;
         this.isSetPasswd = false;
         this.passwd = null;
-        this.roles = null;
+        this.roles = Collections.emptySet();
         this.lastTimeStamp = System.currentTimeMillis();
     }
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java
index 62cfd525cbb..dbfaaa15c2d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java
@@ -257,6 +257,18 @@ public class Auth implements Writable {
         return roles;
     }
 
+    public Set<String> getRoleNamesByUserWithLdap(UserIdentity user, boolean 
showUserDefaultRole) {
+        Set<Role> rolesByUserWithLdap = getRolesByUserWithLdap(user);
+        Set<String> res = 
Sets.newHashSetWithExpectedSize(rolesByUserWithLdap.size());
+        for (Role role : rolesByUserWithLdap) {
+            String roleName = role.getRoleName();
+            if (showUserDefaultRole || 
!roleName.startsWith(RoleManager.DEFAULT_ROLE_PREFIX)) {
+                res.add(roleName);
+            }
+        }
+        return res;
+    }
+
     public List<UserIdentity> getUserIdentityForLdap(String remoteUser, String 
remoteHost) {
         return userManager.getUserIdentityUncheckPasswd(remoteUser, 
remoteHost);
     }
@@ -1333,8 +1345,8 @@ public class Auth implements Writable {
             // ============== Password ==============
             userAuthInfo.add(ldapUserInfo.isSetPasswd() ? "Yes" : "No");
             // ============== Roles ==============
-            userAuthInfo.add(ldapUserInfo.getRoles().stream().map(role -> 
role.getRoleName())
-                    .collect(Collectors.joining(",")));
+            
userAuthInfo.add(Joiner.on(",").join(getRoleNamesByUserWithLdap(userIdent,
+                    
ConnectContext.get().getSessionVariable().showUserDefaultRole)));
         } else {
             User user = userManager.getUserByUserIdentity(userIdent);
             if (user == null) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/refresh/RefreshLdapCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/refresh/RefreshLdapCommand.java
index 65f659d1329..f5b89f23d2c 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/refresh/RefreshLdapCommand.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/refresh/RefreshLdapCommand.java
@@ -32,6 +32,8 @@ import org.apache.doris.qe.StmtExecutor;
 
 import com.google.common.base.Strings;
 
+import java.util.Objects;
+
 /**
  * RefreshLdapCommand
  */
@@ -63,12 +65,14 @@ public class RefreshLdapCommand extends Command implements 
ForwardWithSync {
      * validate
      */
     public void validate(ConnectContext ctx) throws UserException {
-        if (isAll || !Strings.isNullOrEmpty(user)) {
+        String currentUser = ctx.getQualifiedUser();
+        if (!isAll && Strings.isNullOrEmpty(user)) {
+            user = currentUser;
+        }
+        if (isAll || !Objects.equals(currentUser, user)) {
             if 
(!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.ADMIN)) {
                 
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"ADMIN");
             }
-        } else {
-            user = ctx.getQualifiedUser();
         }
     }
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/mysql/authenticate/ldap/LdapUserInfoTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/mysql/authenticate/ldap/LdapUserInfoTest.java
new file mode 100644
index 00000000000..24630a3f1cf
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/mysql/authenticate/ldap/LdapUserInfoTest.java
@@ -0,0 +1,30 @@
+// 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.mysql.authenticate.ldap;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class LdapUserInfoTest {
+
+    @Test
+    public void testNonExistUserRoles() {
+        LdapUserInfo u1 = new LdapUserInfo("u1");
+        Assert.assertFalse(u1.getRoles() == null);
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java
index 3498cd53650..f92679fcdc5 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java
@@ -23,8 +23,11 @@ import org.apache.doris.catalog.Env;
 import org.apache.doris.datasource.InternalCatalog;
 import org.apache.doris.utframe.TestWithFeService;
 
+import org.junit.Assert;
 import org.junit.jupiter.api.Test;
 
+import java.util.Set;
+
 public class AuthTest extends TestWithFeService {
     @Override
     protected void runBeforeAll() throws Exception {
@@ -40,9 +43,25 @@ public class AuthTest extends TestWithFeService {
         grantPriv("GRANT GRANT_PRIV ON internal.test.* TO ROLE 'role2';");
 
         grantRole("GRANT 'role1','role2' TO 'u1'@'%'");
-        
Env.getCurrentEnv().getAuth().checkDbPriv(UserIdentity.createAnalyzedUserIdentWithIp("u1",
 "%"),
-                InternalCatalog.INTERNAL_CATALOG_NAME, "test",
-                PrivPredicate.of(PrivBitSet.of(Privilege.GRANT_PRIV, 
Privilege.LOAD_PRIV), Operator.AND));
+        boolean hasPriv = Env.getCurrentEnv().getAuth()
+                .checkDbPriv(UserIdentity.createAnalyzedUserIdentWithIp("u1", 
"%"),
+                        InternalCatalog.INTERNAL_CATALOG_NAME, "test",
+                        PrivPredicate.of(PrivBitSet.of(Privilege.GRANT_PRIV, 
Privilege.LOAD_PRIV), Operator.AND));
+        Assert.assertTrue(hasPriv);
+    }
+
+    @Test
+    public void testGetRoleNamesByUserWithLdap() throws Exception {
+        addUser("u2", true);
+        createRole("role3");
+        createRole("role4");
+
+        grantRole("GRANT 'role3','role4' TO 'u2'@'%'");
+        Set<String> roleNames = Env.getCurrentEnv().getAuth()
+                .getRoleNamesByUserWithLdap(new UserIdentity("u2", "%"), true);
+        Assert.assertEquals(3, roleNames.size());
+        roleNames = 
Env.getCurrentEnv().getAuth().getRoleNamesByUserWithLdap(new UserIdentity("u2", 
"%"), false);
+        Assert.assertEquals(2, roleNames.size());
     }
 
 }
diff --git a/regression-test/suites/auth_p0/test_refresh_ldap_auth.groovy 
b/regression-test/suites/auth_p0/test_refresh_ldap_auth.groovy
new file mode 100644
index 00000000000..81b5c000c8b
--- /dev/null
+++ b/regression-test/suites/auth_p0/test_refresh_ldap_auth.groovy
@@ -0,0 +1,56 @@
+// 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.
+
+import org.junit.Assert;
+
+suite("test_refresh_ldap_auth","p0,auth") {
+    String suiteName = "test_refresh_ldap_auth"
+    String dbName = context.config.getDbNameByFile(context.file)
+    String user = "${suiteName}_user"
+    String pwd = 'C123_567p'
+    try_sql("DROP USER ${user}")
+    sql """CREATE USER '${user}' IDENTIFIED BY '${pwd}'"""
+
+    //cloud-mode
+    if (isCloudMode()) {
+        def clusters = sql " SHOW CLUSTERS; "
+        assertTrue(!clusters.isEmpty())
+        def validCluster = clusters[0][0]
+        sql """GRANT USAGE_PRIV ON CLUSTER `${validCluster}` TO ${user}""";
+    }
+
+    sql """grant select_priv on regression_test to ${user}"""
+
+    connect(user, "${pwd}", context.config.jdbcUrl) {
+        sql """refresh ldap;"""
+        sql """refresh ldap for ${user};"""
+        test {
+            sql """
+                refresh ldap for root;
+                """
+            exception "denied"
+        }
+        test {
+                sql """
+                    refresh ldap all;
+                    """
+                exception "denied"
+            }
+    }
+
+    try_sql("DROP USER ${user}")
+}


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

Reply via email to