details: https://code.openbravo.com/erp/devel/pi/rev/e32f0aa03825 changeset: 34096:e32f0aa03825 user: Asier Lostalé <asier.lostale <at> openbravo.com> date: Tue May 29 16:18:01 2018 +0200 summary: fixes 38651: problems in user locking implementation
Fixes some problems in user locking: 1. When defining an incremental delay for login, it is only possible to set ranges of integer seconds. Increment by 1 second on each failed attempt is too much: it should be possible to define this increment to something smaller than complete seconds. 2. After a failed login attempt, login response is delayed. While in this delay, a database connection is kept open. It would be better to return it to the pool and get another one afterwards. 3. When trying to log in with a non existing user, delay is also correctly applied. The query count number of failed attempts is checking from the beginning of the time. It should check if there was any attempt during the last one day at most. 4. After a user is locked, subsequent login attempts mark it as locked again. diffstat: src/org/openbravo/base/secureApp/LoginUtils.java | 2 +- src/org/openbravo/base/secureApp/UserLock.java | 70 ++++++++++++----------- 2 files changed, 38 insertions(+), 34 deletions(-) diffs (157 lines): diff -r d2cc1590b1a1 -r e32f0aa03825 src/org/openbravo/base/secureApp/LoginUtils.java --- a/src/org/openbravo/base/secureApp/LoginUtils.java Tue May 29 00:27:14 2018 +0530 +++ b/src/org/openbravo/base/secureApp/LoginUtils.java Tue May 29 16:18:01 2018 +0200 @@ -79,7 +79,7 @@ public static String getValidUserId(ConnectionProvider connectionProvider, String login, String unHashedPassword) { try { - // Deley response and check for locked user + // Delay response and check for locked user UserLock lockSettings = new UserLock(login); lockSettings.delayResponse(); if (lockSettings.isLockedUser()) { diff -r d2cc1590b1a1 -r e32f0aa03825 src/org/openbravo/base/secureApp/UserLock.java --- a/src/org/openbravo/base/secureApp/UserLock.java Tue May 29 00:27:14 2018 +0530 +++ b/src/org/openbravo/base/secureApp/UserLock.java Tue May 29 16:18:01 2018 +0200 @@ -11,7 +11,7 @@ * under the License. * The Original Code is Openbravo ERP. * The Initial Developer of the Original Code is Openbravo SL - * All portions are Copyright (C) 2010-2017 Openbravo SL + * All portions are Copyright (C) 2010-2018 Openbravo SL * All Rights Reserved. * Contributor(s): ______________________________________. ************************************************************************ @@ -19,8 +19,10 @@ package org.openbravo.base.secureApp; +import java.util.Calendar; import java.util.Date; import java.util.Properties; +import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import org.hibernate.Query; @@ -38,12 +40,12 @@ public class UserLock { private static Logger log4j = Logger.getLogger(UserLock.class); - private static int delayInc; + private static float delayInc; private static int delayMax; private static int lockAfterTrials; private static boolean lockingConfigured; - private int delay; + private float delay; private String userName; private int numberOfFails; @@ -65,7 +67,7 @@ } try { - delayInc = Integer.parseInt(propInc); + delayInc = Float.parseFloat(propInc); } catch (NumberFormatException e) { log4j.error("Could not set login.trial.delay.increment property " + propInc, e); delayInc = 0; @@ -96,7 +98,8 @@ // Count how many times this user has attempted to login without success long t = System.currentTimeMillis(); - // to improve performance this query is not done as subquery of the main one, see issue #25466 + // to improve performance this query is not done as subquery of the main one, + // see issue #25466 StringBuilder hql = new StringBuilder(); hql.append("select max(s1.creationDate)"); hql.append(" from ADSession s1"); @@ -105,6 +108,12 @@ Query q1 = OBDal.getInstance().getSession().createQuery(hql.toString()); q1.setParameter("name", userName); Date lastFailedAttempt = (Date) q1.list().get(0); + + if (lastFailedAttempt == null) { + Calendar yesterday = Calendar.getInstance(); + yesterday.add(Calendar.DATE, -1); + lastFailedAttempt = yesterday.getTime(); + } log4j.debug("Time taken to check user lock 1st query " + (System.currentTimeMillis() - t)); long t1 = System.currentTimeMillis(); @@ -113,16 +122,11 @@ hql.append(" from ADSession s "); hql.append(" where s.loginStatus='F'"); hql.append(" and s.username = :name"); - if (lastFailedAttempt != null) { - hql.append(" and s.creationDate > :lastFail"); - } else { - hql.append(" and s.creationDate > s.creationDate-1"); - } + hql.append(" and s.creationDate > :lastFail"); + Query q = OBDal.getInstance().getSession().createQuery(hql.toString()); q.setParameter("name", userName); - if (lastFailedAttempt != null) { - q.setParameter("lastFail", lastFailedAttempt); - } + q.setParameter("lastFail", lastFailedAttempt); numberOfFails = ((Long) q.list().get(0)).intValue(); log4j.debug("Time taken to check user lock " + (System.currentTimeMillis() - t) @@ -166,20 +170,17 @@ boolean lockUser = (lockAfterTrials != 0) && (numberOfFails >= lockAfterTrials); log4j.debug("lock: " + lockUser + " -lock after:" + lockAfterTrials + "- fails:" + numberOfFails + " - user:" + user); - if (lockUser) { - // Try to lock the user in database - delay = 0; - if (user != null) { - try { - OBContext.setAdminMode(); + if (lockUser && user != null && !user.isLocked()) { + try { + OBContext.setAdminMode(); - user.setLocked(true); - OBDal.getInstance().flush(); - log4j.warn(userName + " is locked after " + numberOfFails + " failed logins."); - return; - } finally { - OBContext.restorePreviousMode(); - } + // re-attach user to session as it was detached in commit and close + user = OBDal.getInstance().get(User.class, user.getId()); + user.setLocked(true); + OBDal.getInstance().flush(); + log4j.warn(userName + " is locked after " + numberOfFails + " failed logins."); + } finally { + OBContext.restorePreviousMode(); } } } @@ -204,13 +205,16 @@ * attempts failed. */ public void delayResponse() { - if (delay > 0) { - log4j.debug("Delaying response " + delay + " seconds because of the previous login failed."); - try { - Thread.sleep(delay * 1000); - } catch (InterruptedException e) { - log4j.error("Error delaying login response", e); - } + if (delay == 0) { + return; + } + // release DB connection while delaying response + OBDal.getInstance().commitAndClose(); + log4j.debug("Delaying response " + delay + " seconds because of the previous login failed."); + try { + TimeUnit.MILLISECONDS.sleep((long) (delay * 1_000L)); + } catch (InterruptedException e) { + log4j.error("Error delaying login response", e); } } ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Openbravo-commits mailing list Openbravo-commits@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openbravo-commits