Updated Branches:
  refs/heads/4.3 af5f3d567 -> 7884bb8aa

Changed "authenticate" method to return both - result of authentication, and 
action to perform when authentication failed - to the accountManagerImpl. Only 
if authenicators request INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT, the 
incorrect_login_attempts parameter will be increased

Signed-off-by: Alena Prokharchyk <alena.prokharc...@citrix.com>

Conflicts:
        
plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java
        
plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java
        
plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java
        
plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java
        server/src/com/cloud/server/auth/UserAuthenticator.java
        server/src/com/cloud/user/AccountManagerImpl.java


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/7884bb8a
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/7884bb8a
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/7884bb8a

Branch: refs/heads/4.3
Commit: 7884bb8aafe46b880607fda3602c317b2def8e9d
Parents: af5f3d5
Author: Alena Prokharchyk <alena.prokharc...@citrix.com>
Authored: Tue Jan 7 12:15:37 2014 -0800
Committer: Alena Prokharchyk <alena.prokharc...@citrix.com>
Committed: Tue Jan 21 18:09:15 2014 -0800

----------------------------------------------------------------------
 .../cloudstack/ldap/LdapAuthenticator.java      | 31 ++++++----
 .../cloud/server/auth/MD5UserAuthenticator.java | 64 ++++++++++----------
 .../server/auth/PlainTextUserAuthenticator.java | 42 +++++++------
 .../auth/SHA256SaltedUserAuthenticator.java     | 13 ++--
 .../cloud/server/auth/UserAuthenticator.java    | 35 ++++++-----
 .../src/com/cloud/user/AccountManagerImpl.java  | 25 +++++---
 6 files changed, 118 insertions(+), 92 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7884bb8a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java
----------------------------------------------------------------------
diff --git 
a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java
 
b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java
index 559a979..dac917b 100644
--- 
a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java
+++ 
b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java
@@ -25,6 +25,7 @@ import org.apache.log4j.Logger;
 import com.cloud.server.auth.DefaultUserAuthenticator;
 import com.cloud.user.UserAccount;
 import com.cloud.user.dao.UserAccountDao;
+import com.cloud.utils.Pair;
 
 public class LdapAuthenticator extends DefaultUserAuthenticator {
        private static final Logger s_logger = Logger
@@ -46,23 +47,27 @@ public class LdapAuthenticator extends 
DefaultUserAuthenticator {
                _userAccountDao = userAccountDao;
        }
 
-       @Override
-       public boolean authenticate(final String username, final String 
password,
-                       final Long domainId, final Map<String, Object[]> 
requestParameters) {
+    @Override
+    public Pair<Boolean, ActionOnFailedAuthentication> authenticate(final 
String username, final String password, final Long domainId, final Map<String, 
Object[]> requestParameters) {
 
                final UserAccount user = 
_userAccountDao.getUserAccount(username,
                                domainId);
 
-               if (user == null) {
-                       s_logger.debug("Unable to find user with " + username
-                                       + " in domain " + domainId);
-                       return false;
-               } else if (_ldapManager.isLdapEnabled()) {
-                       return _ldapManager.canAuthenticate(username, password);
-               } else {
-                       return false;
-               }
-       }
+        if (user == null) {
+            s_logger.debug("Unable to find user with " + username + " in 
domain " + domainId);
+            return new Pair<Boolean, ActionOnFailedAuthentication>(false, 
null);
+        } else if (_ldapManager.isLdapEnabled()) {
+            boolean result = _ldapManager.canAuthenticate(username, password);
+            ActionOnFailedAuthentication action = null;
+            if (result == false) {
+                action = 
ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT;
+            }
+            return new Pair<Boolean, ActionOnFailedAuthentication>(result, 
action);
+
+        } else {
+            return new Pair<Boolean, ActionOnFailedAuthentication>(false, 
ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
+        }
+    }
 
        @Override
        public String encode(final String password) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7884bb8a/plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java
----------------------------------------------------------------------
diff --git 
a/plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java
 
b/plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java
index 63583af..f8f75ed 100644
--- 
a/plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java
+++ 
b/plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java
@@ -27,6 +27,7 @@ import org.apache.log4j.Logger;
 
 import com.cloud.user.UserAccount;
 import com.cloud.user.dao.UserAccountDao;
+import com.cloud.utils.Pair;
 import com.cloud.utils.exception.CloudRuntimeException;
 
 /**
@@ -34,47 +35,48 @@ import com.cloud.utils.exception.CloudRuntimeException;
  * comparing it against the local database.
  * 
  */
-@Local(value={UserAuthenticator.class})
+@Local(value = {UserAuthenticator.class})
 public class MD5UserAuthenticator extends DefaultUserAuthenticator {
-       public static final Logger s_logger = 
Logger.getLogger(MD5UserAuthenticator.class);
-       
-       @Inject private UserAccountDao _userAccountDao;
-       
-       @Override
-       public boolean authenticate(String username, String password, Long 
domainId, Map<String, Object[]> requestParameters ) {
-               if (s_logger.isDebugEnabled()) {
+    public static final Logger s_logger = 
Logger.getLogger(MD5UserAuthenticator.class);
+
+    @Inject
+    private UserAccountDao _userAccountDao;
+
+    @Override
+    public Pair<Boolean, ActionOnFailedAuthentication> authenticate(String 
username, String password, Long domainId, Map<String, Object[]> 
requestParameters) {
+        if (s_logger.isDebugEnabled()) {
             s_logger.debug("Retrieving user: " + username);
         }
         UserAccount user = _userAccountDao.getUserAccount(username, domainId);
         if (user == null) {
             s_logger.debug("Unable to find user with " + username + " in 
domain " + domainId);
-            return false;
+            return new Pair<Boolean, ActionOnFailedAuthentication>(false, 
null);
         }
-        
+
         if (!user.getPassword().equals(encode(password))) {
             s_logger.debug("Password does not match");
-            return false;
+            return new Pair<Boolean, ActionOnFailedAuthentication>(false, 
ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
         }
-               return true;
-       }
+        return new Pair<Boolean, ActionOnFailedAuthentication>(true, null);
+    }
 
-       public String encode(String password) {
-               MessageDigest md5 = null;
-               try {
-                       md5 = MessageDigest.getInstance("MD5");
-               } catch (NoSuchAlgorithmException e) {
-                       throw new CloudRuntimeException("Unable to hash 
password", e);
-               }
+    public String encode(String password) {
+        MessageDigest md5 = null;
+        try {
+            md5 = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            throw new CloudRuntimeException("Unable to hash password", e);
+        }
 
-               md5.reset();
-               BigInteger pwInt = new BigInteger(1, 
md5.digest(password.getBytes()));
-               String pwStr = pwInt.toString(16);
-               int padding = 32 - pwStr.length();
-               StringBuffer sb = new StringBuffer();
-               for (int i = 0; i < padding; i++) {
-                   sb.append('0'); // make sure the MD5 password is 32 digits 
long
-               }
-               sb.append(pwStr);
-               return sb.toString();
-       }
+        md5.reset();
+        BigInteger pwInt = new BigInteger(1, md5.digest(password.getBytes()));
+        String pwStr = pwInt.toString(16);
+        int padding = 32 - pwStr.length();
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < padding; i++) {
+            sb.append('0'); // make sure the MD5 password is 32 digits long
+        }
+        sb.append(pwStr);
+        return sb.toString();
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7884bb8a/plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java
----------------------------------------------------------------------
diff --git 
a/plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java
 
b/plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java
index 849e82e..6a5f415 100644
--- 
a/plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java
+++ 
b/plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java
@@ -24,36 +24,38 @@ import org.apache.log4j.Logger;
 
 import com.cloud.user.UserAccount;
 import com.cloud.user.dao.UserAccountDao;
+import com.cloud.utils.Pair;
 
-
-@Local(value={UserAuthenticator.class})
+@Local(value = {UserAuthenticator.class})
 public class PlainTextUserAuthenticator extends DefaultUserAuthenticator {
-       public static final Logger s_logger = 
Logger.getLogger(PlainTextUserAuthenticator.class);
-       
-       @Inject private UserAccountDao _userAccountDao;
-       
-       @Override
-       public boolean authenticate(String username, String password, Long 
domainId, Map<String, Object[]> requestParameters ) {
-               if (s_logger.isDebugEnabled()) {
+    public static final Logger s_logger = 
Logger.getLogger(PlainTextUserAuthenticator.class);
+
+    @Inject
+    private UserAccountDao _userAccountDao;
+
+    @Override
+    public Pair<Boolean, ActionOnFailedAuthentication> authenticate(String 
username, String password, Long domainId, Map<String, Object[]> 
requestParameters) {
+        if (s_logger.isDebugEnabled()) {
             s_logger.debug("Retrieving user: " + username);
         }
 
         UserAccount user = _userAccountDao.getUserAccount(username, domainId);
         if (user == null) {
             s_logger.debug("Unable to find user with " + username + " in 
domain " + domainId);
-            return false;
+            return new Pair<Boolean, ActionOnFailedAuthentication>(false, 
null);
         }
-        
+
         if (!user.getPassword().equals(password)) {
             s_logger.debug("Password does not match");
-            return false;
+            return new Pair<Boolean, ActionOnFailedAuthentication>(false, 
ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
         }
-               return true;
-       }
-
-       @Override
-       public String encode(String password) {
-               // Plaintext so no encoding at all
-               return password; 
-       }
+
+        return new Pair<Boolean, ActionOnFailedAuthentication>(true, null);
+    }
+
+    @Override
+    public String encode(String password) {
+        // Plaintext so no encoding at all
+        return password;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7884bb8a/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java
----------------------------------------------------------------------
diff --git 
a/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java
 
b/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java
index 3592ddc..36305f1 100644
--- 
a/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java
+++ 
b/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java
@@ -30,9 +30,10 @@ import org.bouncycastle.util.encoders.Base64;
 
 import com.cloud.user.UserAccount;
 import com.cloud.user.dao.UserAccountDao;
+import com.cloud.utils.Pair;
 import com.cloud.utils.exception.CloudRuntimeException;
 
-@Local(value={UserAuthenticator.class})
+@Local(value = {UserAuthenticator.class})
 public class SHA256SaltedUserAuthenticator extends DefaultUserAuthenticator {
     public static final Logger s_logger = 
Logger.getLogger(SHA256SaltedUserAuthenticator.class);
     private static final String s_defaultPassword = 
"000000000000000000000000000=";
@@ -45,8 +46,7 @@ public class SHA256SaltedUserAuthenticator extends 
DefaultUserAuthenticator {
      * @see 
com.cloud.server.auth.UserAuthenticator#authenticate(java.lang.String, 
java.lang.String, java.lang.Long, java.util.Map)
      */
     @Override
-    public boolean authenticate(String username, String password,
-            Long domainId, Map<String, Object[]> requestParameters) {
+    public Pair<Boolean, ActionOnFailedAuthentication> authenticate(String 
username, String password, Long domainId, Map<String, Object[]> 
requestParameters) {
         if (s_logger.isDebugEnabled()) {
             s_logger.debug("Retrieving user: " + username);
         }
@@ -72,7 +72,12 @@ public class SHA256SaltedUserAuthenticator extends 
DefaultUserAuthenticator {
         try {
             String hashedPassword = encode(password, salt);
             /* constantTimeEquals comes first in boolean since we need to 
thwart timing attacks */
-            return constantTimeEquals(realPassword, hashedPassword) && 
realUser;
+            boolean result = constantTimeEquals(realPassword, hashedPassword) 
&& realUser;
+            ActionOnFailedAuthentication action = null;
+            if (!result && realUser) {
+                action = 
ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT;
+            }
+            return new Pair<Boolean, ActionOnFailedAuthentication>(result, 
action);
         } catch (NoSuchAlgorithmException e) {
             throw new CloudRuntimeException("Unable to hash password", e);
         } catch (UnsupportedEncodingException e) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7884bb8a/server/src/com/cloud/server/auth/UserAuthenticator.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/server/auth/UserAuthenticator.java 
b/server/src/com/cloud/server/auth/UserAuthenticator.java
index 95c4f0e..cc527b8 100644
--- a/server/src/com/cloud/server/auth/UserAuthenticator.java
+++ b/server/src/com/cloud/server/auth/UserAuthenticator.java
@@ -18,26 +18,29 @@ package com.cloud.server.auth;
 
 import java.util.Map;
 
+import com.cloud.utils.Pair;
 import com.cloud.utils.component.Adapter;
 
 /**
  * which UserAuthenticator to user in components.xml.
- * 
  */
 public interface UserAuthenticator extends Adapter {
-       
-       /**
-        * 
-        * @param username
-        * @param password
-        * @param domainId
-        * @return true if the user has been successfully authenticated, false 
otherwise
-        */
-       public boolean authenticate(String username, String password, Long 
domainId, Map<String, Object[]> requestParameters);
-       
-       /**
-        * @param password
-        * @return the encoded password
-        */
-       public String encode(String password);
+    public enum ActionOnFailedAuthentication {
+        INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT;
+    }
+
+    /**
+     *
+     * @param username
+     * @param password
+     * @param domainId
+     * @return the pair of 2 booleans - first identifies the success of 
authenciation, the second - whether to increase incorrect login attempts count 
in case of failed authentication
+     */
+    public Pair<Boolean, ActionOnFailedAuthentication> authenticate(String 
username, String password, Long domainId, Map<String, Object[]> 
requestParameters);
+
+    /**
+     * @param password
+     * @return the encoded password
+     */
+    public String encode(String password);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7884bb8a/server/src/com/cloud/user/AccountManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/user/AccountManagerImpl.java 
b/server/src/com/cloud/user/AccountManagerImpl.java
index 0655f8c..d367653 100755
--- a/server/src/com/cloud/user/AccountManagerImpl.java
+++ b/server/src/com/cloud/user/AccountManagerImpl.java
@@ -21,6 +21,7 @@ import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -110,6 +111,7 @@ import com.cloud.projects.ProjectVO;
 import com.cloud.projects.dao.ProjectAccountDao;
 import com.cloud.projects.dao.ProjectDao;
 import com.cloud.server.auth.UserAuthenticator;
+import com.cloud.server.auth.UserAuthenticator.ActionOnFailedAuthentication;
 import com.cloud.storage.VMTemplateVO;
 import com.cloud.storage.Volume;
 import com.cloud.storage.VolumeApiService;
@@ -1960,13 +1962,19 @@ public class AccountManagerImpl extends ManagerBase 
implements AccountManager, M
         }
 
         boolean authenticated = false;
+        HashSet<ActionOnFailedAuthentication> actionsOnFailedAuthenticaion = 
new HashSet<ActionOnFailedAuthentication>();
         for (UserAuthenticator authenticator : _userAuthenticators) {
-            if (authenticator.authenticate(username, password, domainId, 
requestParameters)) {
+            Pair<Boolean, ActionOnFailedAuthentication> result = 
authenticator.authenticate(username, password, domainId, requestParameters);
+            if (result.first()) {
                 authenticated = true;
                 break;
+            } else if (result.second() != null) {
+                actionsOnFailedAuthenticaion.add(result.second());
             }
         }
 
+        boolean updateIncorrectLoginCount = 
actionsOnFailedAuthenticaion.contains(ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
+
         if (authenticated) {
             UserAccount userAccount = _userAccountDao.getUserAccount(username, 
domainId);
             if (userAccount == null) {
@@ -2003,13 +2011,14 @@ public class AccountManagerImpl extends ManagerBase 
implements AccountManager, M
                     if (!isInternalAccount(userAccount.getType())) {
                         // Internal accounts are not disabled
                         int attemptsMade = userAccount.getLoginAttempts() + 1;
-                        if (attemptsMade < _allowedLoginAttempts) {
-                            updateLoginAttempts(userAccount.getId(), 
attemptsMade, false);
-                            s_logger.warn("Login attempt failed. You have " + 
(_allowedLoginAttempts - attemptsMade) + " attempt(s) remaining");
-                        } else {
-                            updateLoginAttempts(userAccount.getId(), 
_allowedLoginAttempts, true);
-                            s_logger.warn("User " + userAccount.getUsername() 
+ " has been disabled due to multiple failed login attempts." +
-                                    " Please contact admin.");
+                        if (updateIncorrectLoginCount) {
+                            if (attemptsMade < _allowedLoginAttempts) {
+                                updateLoginAttempts(userAccount.getId(), 
attemptsMade, false);
+                                s_logger.warn("Login attempt failed. You have 
" + (_allowedLoginAttempts - attemptsMade) + " attempt(s) remaining");
+                            } else {
+                                updateLoginAttempts(userAccount.getId(), 
_allowedLoginAttempts, true);
+                                s_logger.warn("User " + 
userAccount.getUsername() + " has been disabled due to multiple failed login 
attempts." + " Please contact admin.");
+                            }
                         }
                     }
                 } else {

Reply via email to