You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by ma...@apache.org on 2022/03/21 07:36:30 UTC

[ranger] branch master updated: RANGER-2362: lockout login after too many failure attempts

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

madhan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/master by this push:
     new 943a3a0  RANGER-2362: lockout login after too many failure attempts
943a3a0 is described below

commit 943a3a0bb02e7897c7b621994c244733c9dc0d2b
Author: ZhouTianling <zh...@sensorsdata.cn>
AuthorDate: Thu Mar 17 20:01:04 2022 +0800

    RANGER-2362: lockout login after too many failure attempts
    
    Signed-off-by: Madhan Neethiraj <ma...@apache.org>
---
 .../java/org/apache/ranger/biz/SessionMgr.java     | 29 +++++++++++++++++++++-
 .../org/apache/ranger/db/XXAuthSessionDao.java     | 13 ++++++++++
 .../handler/RangerAuthenticationProvider.java      | 15 +++++++++++
 .../security/listener/SpringEventListener.java     | 16 +++++++++++-
 .../main/resources/META-INF/jpa_named_queries.xml  | 12 +++++++++
 .../conf.dist/ranger-admin-default-site.xml        | 13 ++++++++++
 6 files changed, 96 insertions(+), 2 deletions(-)

diff --git a/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java b/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java
index 6b002cf..3f56448 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java
@@ -44,6 +44,7 @@ import org.apache.ranger.common.SearchCriteria;
 import org.apache.ranger.common.StringUtil;
 import org.apache.ranger.common.UserSessionBase;
 import org.apache.ranger.db.RangerDaoManager;
+import org.apache.ranger.db.XXAuthSessionDao;
 import org.apache.ranger.entity.XXAuthSession;
 import org.apache.ranger.entity.XXPortalUser;
 import org.apache.ranger.entity.XXPortalUserRole;
@@ -453,7 +454,33 @@ public class SessionMgr {
 		VXAuthSession vXAuthSession = authSessionService.populateViewBean(xXAuthSession);
 		return vXAuthSession;
 	}
-	
+
+	/**
+	 * Check whether the user failed to log in so many times that we need to lock it for
+	 * a while. The current limit of is to fail at most n times in a sliding time window,
+	 * otherwise the login verification will not be performed in the future.
+	 * @param loginId
+	 * @return
+	 */
+	public boolean isLoginIdLocked(String loginId) {
+		boolean ret             = false;
+		boolean autoLockEnabled = PropertiesUtil.getBooleanProperty("ranger.admin.login.autolock.enabled", true);
+
+		if (autoLockEnabled) {
+			int  windowSeconds    = PropertiesUtil.getIntProperty("ranger.admin.login.autolock.window.seconds", 300);
+			int  maxFailuresCount = PropertiesUtil.getIntProperty("ranger.admin.login.autolock.maxfailure", 5);
+			long failuresCount    = daoManager.getXXAuthSession().getRecentAuthFailureCountByLoginId(loginId, windowSeconds);
+
+			ret = failuresCount >= maxFailuresCount;
+
+			if (logger.isDebugEnabled()) {
+				logger.debug("isLoginIdLocked(loginId={}): windowSeconds={}, maxFailuresCount={}, failuresCount={}, ret={}", loginId, windowSeconds, maxFailuresCount, failuresCount, ret);
+			}
+		}
+
+		return ret;
+	}
+
 	public boolean isValidXAUser(String loginId) {
 		XXPortalUser pUser = daoManager.getXXPortalUser().findByLoginId(loginId);
 		if (pUser == null) {
diff --git a/security-admin/src/main/java/org/apache/ranger/db/XXAuthSessionDao.java b/security-admin/src/main/java/org/apache/ranger/db/XXAuthSessionDao.java
index b0270e9..934d258 100644
--- a/security-admin/src/main/java/org/apache/ranger/db/XXAuthSessionDao.java
+++ b/security-admin/src/main/java/org/apache/ranger/db/XXAuthSessionDao.java
@@ -19,10 +19,12 @@
 
  package org.apache.ranger.db;
 
+import java.util.Date;
 import java.util.List;
 
 import javax.persistence.NoResultException;
 
+import org.apache.ranger.common.DateUtil;
 import org.apache.ranger.common.db.BaseDao;
 import org.apache.ranger.entity.XXAuthSession;
 import org.springframework.stereotype.Service;
@@ -63,5 +65,16 @@ public class XXAuthSessionDao extends BaseDao<XXAuthSession> {
 			return null;
 		}
 	}
+
+	public long getRecentAuthFailureCountByLoginId(String loginId, int timeRangezSecond){
+		Date authWindowStartTime = new Date(DateUtil.getUTCDate().getTime() - timeRangezSecond * 1000);
+
+		return getEntityManager()
+				.createNamedQuery("XXAuthSession.getRecentAuthFailureCountByLoginId", Long.class)
+				.setParameter("loginId", loginId)
+				.setParameter("authWindowStartTime", authWindowStartTime)
+				.getSingleResult();
+	}
+
 }
 
diff --git a/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java b/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java
index 8f7abbe..efd5417 100644
--- a/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java
+++ b/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java
@@ -41,6 +41,7 @@ import org.springframework.ldap.core.support.LdapContextSource;
 import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.authentication.AuthenticationServiceException;
 import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.LockedException;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.authentication.jaas.DefaultJaasAuthenticationProvider;
 import org.springframework.security.authentication.jaas.memory.InMemoryConfiguration;
@@ -62,6 +63,7 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.apache.ranger.biz.UserMgr;
+import org.apache.ranger.biz.SessionMgr;
 
 
 
@@ -73,7 +75,12 @@ public class RangerAuthenticationProvider implements AuthenticationProvider {
 
 	@Autowired
 	UserMgr userMgr;
+
+	@Autowired
+	SessionMgr sessionMgr;
+
 	private static final Logger logger = LoggerFactory.getLogger(RangerAuthenticationProvider.class);
+
 	private String rangerAuthenticationMethod;
 
 	private LdapAuthenticator authenticator;
@@ -137,6 +144,14 @@ public class RangerAuthenticationProvider implements AuthenticationProvider {
 					return authentication;
 				}
 			}
+
+			// Following are JDBC
+			if (sessionMgr.isLoginIdLocked(authentication.getName())) {
+				logger.debug("Failed to authenticate since user account is locked");
+
+				throw new LockedException(String.format("User account {} is locked", authentication.getName()));
+			}
+
 			if (this.isFipsEnabled) {
 				try {
 					authentication = getJDBCAuthentication(authentication,"");
diff --git a/security-admin/src/main/java/org/apache/ranger/security/listener/SpringEventListener.java b/security-admin/src/main/java/org/apache/ranger/security/listener/SpringEventListener.java
index af5622a..9b048a0 100644
--- a/security-admin/src/main/java/org/apache/ranger/security/listener/SpringEventListener.java
+++ b/security-admin/src/main/java/org/apache/ranger/security/listener/SpringEventListener.java
@@ -30,6 +30,7 @@ import org.springframework.security.authentication.event.AbstractAuthenticationE
 import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
 import org.springframework.security.authentication.event.AuthenticationFailureDisabledEvent;
 import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
+import org.springframework.security.authentication.event.AuthenticationFailureLockedEvent;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.authentication.WebAuthenticationDetails;
 
@@ -49,6 +50,8 @@ public class SpringEventListener implements
 		process((AuthenticationSuccessEvent) event);
 	    } else if (event instanceof AuthenticationFailureBadCredentialsEvent) {
 		process((AuthenticationFailureBadCredentialsEvent) event);
+	    } else if (event instanceof AuthenticationFailureLockedEvent) {
+		process((AuthenticationFailureLockedEvent) event);
 	    } else if (event instanceof AuthenticationFailureDisabledEvent) {
 		process((AuthenticationFailureDisabledEvent) event);
 	    }
@@ -93,6 +96,17 @@ public class SpringEventListener implements
 		remoteAddress, sessionId);
     }
 
+    protected void process(AuthenticationFailureLockedEvent authFailEvent) {
+		Authentication           auth          = authFailEvent.getAuthentication();
+		WebAuthenticationDetails details       = (WebAuthenticationDetails) auth.getDetails();
+		String                   remoteAddress = details != null ? details.getRemoteAddress() : "";
+		String                   sessionId     = details != null ? details.getSessionId() : "";
+
+		logger.info("Login Unsuccessful:" + auth.getName() + " | Ip Address:" + remoteAddress + " | User Locked");
+
+		sessionMgr.processFailureLogin(XXAuthSession.AUTH_STATUS_LOCKED, XXAuthSession.AUTH_TYPE_PASSWORD, auth.getName(), remoteAddress, sessionId);
+	}
+
     protected void process(AuthenticationFailureDisabledEvent authFailEvent) {
 	Authentication auth = authFailEvent.getAuthentication();
 	WebAuthenticationDetails details = (WebAuthenticationDetails) auth
@@ -102,7 +116,7 @@ public class SpringEventListener implements
 	String sessionId = details != null ? details.getSessionId() : "";
 
 	logger.info("Login Unsuccessful:" + auth.getName() + " | Ip Address:"
-		+ remoteAddress);
+		+ remoteAddress + " | User Disabled");
 
 	sessionMgr.processFailureLogin(XXAuthSession.AUTH_STATUS_DISABLED,
 		XXAuthSession.AUTH_TYPE_PASSWORD, auth.getName(),
diff --git a/security-admin/src/main/resources/META-INF/jpa_named_queries.xml b/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
index b56cd26..2f50d71 100755
--- a/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
+++ b/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
@@ -24,6 +24,18 @@
 			   WHERE obj.extSessionId = :sessionId
 		</query>
 	</named-query>
+	<named-query name="XXAuthSession.getRecentAuthFailureCountByLoginId">
+		<query>SELECT COUNT(1) FROM XXAuthSession obj
+			WHERE obj.loginId = :loginId
+			  AND obj.authStatus != 1
+			  AND obj.createTime &gt; COALESCE(
+			    (SELECT MAX(obj2.createTime) FROM XXAuthSession obj2
+			      WHERE obj2.loginId = :loginId
+			        AND obj2.authStatus = 1
+			        AND obj2.createTime &gt; :authWindowStartTime),
+			    :authWindowStartTime)
+		</query>
+	</named-query>
 
 	<!-- XXPortalUser -->
 	<named-query name="XXPortalUser.findByEmailAddress">
diff --git a/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml b/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
index 2471f6a..e2bfc8f 100644
--- a/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
+++ b/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
@@ -157,6 +157,19 @@
 		<value>1</value>
 	</property>
 
+<!-- #auto lock when too many failed logon attempts -->
+	<property>
+		<name>ranger.admin.login.autolock.enabled</name>
+		<value>true</value>
+	</property>
+	<property>
+		<name>ranger.admin.login.autolock.window.seconds</name>
+		<value>300</value>
+	</property>
+	<property>
+		<name>ranger.admin.login.autolock.maxfailure</name>
+		<value>5</value>
+	</property>
 
 <!-- #hacks -->
 	<property>