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

sureshanaparti pushed a commit to branch 4.20
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.20 by this push:
     new ed6ee6b704f Mark LDAP user query timeout as incorrect login instead of 
disabling user immediately (#11220)
ed6ee6b704f is described below

commit ed6ee6b704ff84eb0efcec92fbcec518a90d30d0
Author: Suresh Kumar Anaparti <sureshkumar.anapa...@gmail.com>
AuthorDate: Fri Jul 25 19:31:43 2025 +0530

    Mark LDAP user query timeout as incorrect login instead of disabling user 
immediately (#11220)
    
    * Mark LDAP user query timeout as incorrect login instead of disabling user 
immediately
    
    * code improvements
---
 .../cloudstack/api/command/LdapListUsersCmd.java   |  5 +-
 .../apache/cloudstack/ldap/LdapAuthenticator.java  | 58 ++++++++++++++--------
 .../apache/cloudstack/ldap/LdapManagerImpl.java    | 22 ++++----
 3 files changed, 50 insertions(+), 35 deletions(-)

diff --git 
a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapListUsersCmd.java
 
b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapListUsersCmd.java
index e5d434d3810..6bfc70ea139 100644
--- 
a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapListUsersCmd.java
+++ 
b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapListUsersCmd.java
@@ -140,10 +140,11 @@ public class LdapListUsersCmd extends BaseListCmd {
         try {
             final List<LdapUser> users = _ldapManager.getUsers(domainId);
             ldapResponses = createLdapUserResponse(users);
-//            now filter and annotate
+            // now filter and annotate
             ldapResponses = applyUserFilter(ldapResponses);
         } catch (final NoLdapUserMatchingQueryException ex) {
-            // ok, we'll make do with the empty list ldapResponses = new 
ArrayList<LdapUserResponse>();
+            logger.debug(ex.getMessage());
+            // ok, we'll make do with the empty list
         } finally {
             response.setResponses(ldapResponses);
             response.setResponseName(getCommandName());
diff --git 
a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapAuthenticator.java
 
b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapAuthenticator.java
index b8509881594..36c663566cb 100644
--- 
a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapAuthenticator.java
+++ 
b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapAuthenticator.java
@@ -45,6 +45,8 @@ public class LdapAuthenticator extends AdapterBase implements 
UserAuthenticator
     @Inject
     private AccountManager _accountManager;
 
+    private static final String LDAP_READ_TIMED_OUT_MESSAGE = "LDAP response 
read timed out";
+
     public LdapAuthenticator() {
         super();
     }
@@ -74,8 +76,8 @@ public class LdapAuthenticator extends AdapterBase implements 
UserAuthenticator
                     return rc;
                 }
                 List<LdapTrustMapVO> ldapTrustMapVOs = 
getLdapTrustMapVOS(domainId);
-                if(ldapTrustMapVOs != null && ldapTrustMapVOs.size() > 0) {
-                    if(ldapTrustMapVOs.size() == 1 && 
ldapTrustMapVOs.get(0).getAccountId() == 0) {
+                if (ldapTrustMapVOs != null && ldapTrustMapVOs.size() > 0) {
+                    if (ldapTrustMapVOs.size() == 1 && 
ldapTrustMapVOs.get(0).getAccountId() == 0) {
                         if (logger.isTraceEnabled()) {
                             logger.trace("We have a single mapping of a domain 
to an ldap group or ou");
                         }
@@ -125,11 +127,11 @@ public class LdapAuthenticator extends AdapterBase 
implements UserAuthenticator
             mappedGroups.retainAll(memberships);
             tracelist("actual groups for " + username, mappedGroups);
             // check membership, there must be only one match in this domain
-            if(ldapUser.isDisabled()) {
+            if (ldapUser.isDisabled()) {
                 logAndDisable(userAccount, "attempt to log on using disabled 
ldap user " + userAccount.getUsername(), false);
-            } else if(mappedGroups.size() > 1) {
+            } else if (mappedGroups.size() > 1) {
                 logAndDisable(userAccount, "user '" + username + "' is mapped 
to more then one account in domain and will be disabled.", false);
-            } else if(mappedGroups.size() < 1) {
+            } else if (mappedGroups.size() < 1) {
                 logAndDisable(userAccount, "user '" + username + "' is not 
mapped to an account in domain and will be removed.", true);
             } else {
                 // a valid ldap configured user exists
@@ -137,12 +139,12 @@ public class LdapAuthenticator extends AdapterBase 
implements UserAuthenticator
                 // we could now assert that ldapTrustMapVOs.contains(mapping);
                 // createUser in Account can only be done by account name not 
by account id;
                 Account account = 
_accountManager.getAccount(mapping.getAccountId());
-                if(null == account) {
+                if (null == account) {
                     throw new CloudRuntimeException(String.format("account for 
user (%s) not found by id %d", username, mapping.getAccountId()));
                 }
                 String accountName = account.getAccountName();
                 rc.first(_ldapManager.canAuthenticate(ldapUser.getPrincipal(), 
password, domainId));
-                if (! rc.first()) {
+                if (!rc.first()) {
                     
rc.second(ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
                 }
                 // for security reasons we keep processing on faulty login 
attempt to not give a way information on userid existence
@@ -162,7 +164,7 @@ public class LdapAuthenticator extends AdapterBase 
implements UserAuthenticator
                     userAccount = 
_accountManager.getUserAccountById(user.getId());
                 } else {
                     // not a new user, check if mapped group has changed
-                    if(userAccount.getAccountId() != mapping.getAccountId()) {
+                    if (userAccount.getAccountId() != mapping.getAccountId()) {
                         final Account mappedAccount = 
_accountManager.getAccount(mapping.getAccountId());
                         if (mappedAccount == null || 
mappedAccount.getRemoved() != null) {
                             throw new CloudRuntimeException("Mapped account 
for users does not exist. Please contact your administrator.");
@@ -174,12 +176,21 @@ public class LdapAuthenticator extends AdapterBase 
implements UserAuthenticator
             }
         } catch (NoLdapUserMatchingQueryException e) {
             logger.debug(e.getMessage());
-            disableUserInCloudStack(userAccount);
+            processLdapUserErrorMessage(userAccount, e.getMessage(), rc);
         }
 
         return rc;
     }
 
+    private void processLdapUserErrorMessage(UserAccount user, String 
errorMessage, Pair<Boolean, ActionOnFailedAuthentication> rc) {
+        if (StringUtils.isNotEmpty(errorMessage) && 
errorMessage.contains(LDAP_READ_TIMED_OUT_MESSAGE) && !rc.first()) {
+            
rc.second(ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
+        } else {
+            // no user in ldap ==>> disable user in cloudstack
+            disableUserInCloudStack(user);
+        }
+    }
+
     private void tracelist(String msg, List<String> listToTrace) {
         if (logger.isTraceEnabled()) {
             StringBuilder logMsg = new StringBuilder();
@@ -197,7 +208,7 @@ public class LdapAuthenticator extends AdapterBase 
implements UserAuthenticator
         if (logger.isInfoEnabled()) {
             logger.info(msg);
         }
-        if(remove) {
+        if (remove) {
             removeUserInCloudStack(userAccount);
         } else {
             disableUserInCloudStack(userAccount);
@@ -229,23 +240,22 @@ public class LdapAuthenticator extends AdapterBase 
implements UserAuthenticator
             processLdapUser(password, domainId, user, rc, ldapUser, 
accountType);
         } catch (NoLdapUserMatchingQueryException e) {
             logger.debug(e.getMessage());
-            // no user in ldap ==>> disable user in cloudstack
-            disableUserInCloudStack(user);
+            processLdapUserErrorMessage(user, e.getMessage(), rc);
         }
         return rc;
     }
 
     private void processLdapUser(String password, Long domainId, UserAccount 
user, Pair<Boolean, ActionOnFailedAuthentication> rc, LdapUser ldapUser, 
Account.Type accountType) {
-        if(!ldapUser.isDisabled()) {
+        if (!ldapUser.isDisabled()) {
             rc.first(_ldapManager.canAuthenticate(ldapUser.getPrincipal(), 
password, domainId));
-            if(rc.first()) {
-                if(user == null) {
+            if (rc.first()) {
+                if (user == null) {
                     // import user to cloudstack
                     createCloudStackUserAccount(ldapUser, domainId, 
accountType);
                 } else {
                     enableUserInCloudStack(user);
                 }
-            } else if(user != null) {
+            } else if (user != null) {
                 
rc.second(ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
             }
         } else {
@@ -264,30 +274,34 @@ public class LdapAuthenticator extends AdapterBase 
implements UserAuthenticator
      */
     Pair<Boolean, ActionOnFailedAuthentication> authenticate(String username, 
String password, Long domainId, UserAccount user) {
         boolean result = false;
+        boolean timedOut = false;
 
-        if(user != null ) {
+        if (user != null ) {
             try {
                 LdapUser ldapUser = _ldapManager.getUser(username, domainId);
-                if(!ldapUser.isDisabled()) {
+                if (!ldapUser.isDisabled()) {
                     result = 
_ldapManager.canAuthenticate(ldapUser.getPrincipal(), password, domainId);
                 } else {
                     logger.debug("user with principal "+ 
ldapUser.getPrincipal() + " is disabled in ldap");
                 }
             } catch (NoLdapUserMatchingQueryException e) {
                 logger.debug(e.getMessage());
+                if (e.getMessage().contains(LDAP_READ_TIMED_OUT_MESSAGE)) {
+                    timedOut = true;
+                }
             }
         }
-        return processResultAndAction(user, result);
+        return processResultAndAction(user, result, timedOut);
     }
 
-    private Pair<Boolean, ActionOnFailedAuthentication> 
processResultAndAction(UserAccount user, boolean result) {
-        return (!result && user != null) ?
+    private Pair<Boolean, ActionOnFailedAuthentication> 
processResultAndAction(UserAccount user, boolean result, boolean timedOut) {
+        return (!result && (user != null || timedOut)) ?
                 new Pair<Boolean, ActionOnFailedAuthentication>(result, 
ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT):
                 new Pair<Boolean, ActionOnFailedAuthentication>(result, null);
     }
 
     private void enableUserInCloudStack(UserAccount user) {
-        if(user != null && 
(user.getState().equalsIgnoreCase(Account.State.DISABLED.toString()))) {
+        if (user != null && 
(user.getState().equalsIgnoreCase(Account.State.DISABLED.toString()))) {
             _accountManager.enableUser(user.getId());
         }
     }
diff --git 
a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapManagerImpl.java
 
b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapManagerImpl.java
index 2394c0b3d1e..05b8578bb42 100644
--- 
a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapManagerImpl.java
+++ 
b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapManagerImpl.java
@@ -166,7 +166,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase 
implements LdapManag
 
     private LdapConfigurationResponse addConfigurationInternal(final String 
hostname, int port, final Long domainId) throws InvalidParameterValueException {
         // TODO evaluate what the right default should be
-        if(port <= 0) {
+        if (port <= 0) {
             port = 389;
         }
 
@@ -210,7 +210,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase 
implements LdapManag
             // TODO return the right account for this user
             final LdapContext context = 
_ldapContextFactory.createUserContext(principal, password, domainId);
             closeContext(context);
-            if(logger.isTraceEnabled()) {
+            if (logger.isTraceEnabled()) {
                 logger.trace(String.format("User(%s) authenticated for 
domain(%s)", principal, domainId));
             }
             return true;
@@ -234,7 +234,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase 
implements LdapManag
     @Override
     public LdapConfigurationResponse createLdapConfigurationResponse(final 
LdapConfigurationVO configuration) {
         String domainUuid = null;
-        if(configuration.getDomainId() != null) {
+        if (configuration.getDomainId() != null) {
             DomainVO domain = domainDao.findById(configuration.getDomainId());
             if (domain != null) {
                 domainUuid = domain.getUuid();
@@ -303,8 +303,8 @@ public class LdapManagerImpl extends ComponentLifecycleBase 
implements LdapManag
             return 
_ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(null)).getUser(escapedUsername,
 context, domainId);
 
         } catch (NamingException | IOException e) {
-            logger.debug("ldap Exception: ",e);
-            throw new NoLdapUserMatchingQueryException("No Ldap User found for 
username: "+username);
+            logger.debug("LDAP Exception: ", e);
+            throw new NoLdapUserMatchingQueryException("Unable to find LDAP 
User for username: " + username + ", due to " + e.getMessage());
         } finally {
             closeContext(context);
         }
@@ -324,8 +324,8 @@ public class LdapManagerImpl extends ComponentLifecycleBase 
implements LdapManag
             LdapUserManager userManagerFactory = 
_ldapUserManagerFactory.getInstance(ldapProvider);
             return userManagerFactory.getUser(escapedUsername, type, name, 
context, domainId);
         } catch (NamingException | IOException e) {
-            logger.debug("ldap Exception: ",e);
-            throw new NoLdapUserMatchingQueryException("No Ldap User found for 
username: "+username + " in group: " + name + " of type: " + type);
+            logger.debug("LDAP Exception: ", e);
+            throw new NoLdapUserMatchingQueryException("Unable to find LDAP 
User for username: " + username + " in group: " + name + " of type: " + type + 
", due to " + e.getMessage());
         } finally {
             closeContext(context);
         }
@@ -338,7 +338,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase 
implements LdapManag
             context = _ldapContextFactory.createBindContext(domainId);
             return 
_ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(domainId)).getUsers(context,
 domainId);
         } catch (NamingException | IOException e) {
-            logger.debug("ldap Exception: ",e);
+            logger.debug("LDAP Exception: ", e);
             throw new NoLdapUserMatchingQueryException("*");
         } finally {
             closeContext(context);
@@ -352,7 +352,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase 
implements LdapManag
             context = _ldapContextFactory.createBindContext(domainId);
             return 
_ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(domainId)).getUsersInGroup(groupName,
 context, domainId);
         } catch (NamingException | IOException e) {
-            logger.debug("ldap NamingException: ",e);
+            logger.debug("LDAP Exception: ", e);
             throw new NoLdapUserMatchingQueryException("groupName=" + 
groupName);
         } finally {
             closeContext(context);
@@ -390,7 +390,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase 
implements LdapManag
             final String escapedUsername = 
LdapUtils.escapeLDAPSearchFilter(username);
             return 
_ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(null)).getUsers("*"
 + escapedUsername + "*", context, null);
         } catch (NamingException | IOException e) {
-            logger.debug("ldap Exception: ",e);
+            logger.debug("LDAP Exception: ",e);
             throw new NoLdapUserMatchingQueryException(username);
         } finally {
             closeContext(context);
@@ -481,7 +481,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase 
implements LdapManag
     private void clearOldAccountMapping(LinkAccountToLdapCmd cmd) {
         //        first find if exists log warning and update
         LdapTrustMapVO oldVo = 
_ldapTrustMapDao.findGroupInDomain(cmd.getDomainId(), cmd.getLdapDomain());
-        if(oldVo != null) {
+        if (oldVo != null) {
             // deal with edge cases, i.e. check if the old account is indeed 
deleted etc.
             if (oldVo.getAccountId() != 0l) {
                 AccountVO oldAcount = 
accountDao.findByIdIncludingRemoved(oldVo.getAccountId());

Reply via email to