package jeffaschenk.commons.container.security.monitor; import jeffaschenk.commons.container.security.object.SecurityServiceMonitorObject; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.servlet.http.HttpServletRequest; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * SecurityServiceMonitor * Security Service Monitor for collecting statistics and information regarding * usage of the Login principal. * * @author jeffaschenk@gmail.com */ @Service("securityServiceMonitor") public class SecurityServiceMonitorImpl implements SecurityServiceMonitor { /** * Logging Constant <code>log</code> */ protected static Log log = LogFactory .getLog(SecurityServiceMonitor.class); /** * Monitor by Request and Principal */ private Map<String, SecurityServiceMonitorObject> securityServiceMonitorObjects; /** * Global Properties and Environment Injected */ @Value("#{systemEnvironmentProperties['security.checkFailedLoginAttempts']}") private boolean checkFailedLoginAttempts; @Value("#{systemEnvironmentProperties['security.maxFailedLoginAttempts']}") private long maxFailedLoginAttempts; @Value("#{systemEnvironmentProperties['security.failedLoginSuspendedTimeout']}") private int failedLoginSuspendedTimeout; // In Seconds /** * Initialize the Singleton Session Cache. */ @PostConstruct public synchronized void init() { // ************************************ // Initialization log.info("SecurityServiceMonitor starting to Initialize."); // *************************************** // Create an In-Memory Cache for various // statistics such as: // Users Log-in Attempts. // this.securityServiceMonitorObjects = new HashMap<String, SecurityServiceMonitorObject>(); // *************************************** // Show Configuration Environment log.info("Check Failed Log-In attempts has been " + ((this.checkFailedLoginAttempts) ? "enabled." : "disabled.")); if (this.checkFailedLoginAttempts) { log.info("Check Failed Log-In Maximum attempts before Lock Out is " + this.maxFailedLoginAttempts + " attempts."); log.info("Check Failed Log-In Lock-Out or Suspend Time " + this.failedLoginSuspendedTimeout + " seconds."); } // *************************************** // Proceed with additional Initialization log.info("SecurityServiceMonitor has been Initialized."); } /** * <p/> * Destroy Implemented Spring Interface Method, invoked when bean is removed * from container. */ @PreDestroy public void destroy() { log .info("SecurityServiceMonitor has been Removed from the Container."); securityServiceMonitorObjects.clear(); securityServiceMonitorObjects = null; } /** * Monitor Login attempt Requests * * @param principal * @param request */ @Override public void monitorLogInAttemptRequest(Object principal, HttpServletRequest request) { if (principal == null) { return; } SecurityServiceMonitorObject securityServiceMonitorObject = null; if (securityServiceMonitorObjects.containsKey(principal.toString().toLowerCase())) { // ********************************** // Update Existing Monitor Object. securityServiceMonitorObject = securityServiceMonitorObjects.get(principal.toString().toLowerCase()); if (securityServiceMonitorObject != null) { securityServiceMonitorObject.incrementTotalAttempts(); securityServiceMonitorObject.setLastLogInAttempt(new Date()); securityServiceMonitorObject.setLocalAddress(request.getLocalAddr()); securityServiceMonitorObject.setLocalName(request.getLocalName()); securityServiceMonitorObject.setLocalPort(request.getLocalPort()); securityServiceMonitorObject.setRemoteAddress(request.getRemoteAddr()); securityServiceMonitorObject.setRemoteName(request.getRemoteHost()); securityServiceMonitorObject.setRemotePort(request.getRemotePort()); } } // ********************************** // Add a new Monitor Object for // future checks. if (securityServiceMonitorObject == null) { securityServiceMonitorObject = new SecurityServiceMonitorObject(principal, request.getContextPath(), request.getLocalAddr(), request.getLocalName(), request.getLocalPort(), request.getRemoteAddr(), request.getRemoteHost(), request.getRemotePort()); } securityServiceMonitorObjects.put(principal.toString().toLowerCase(), securityServiceMonitorObject); } /** * Monitor Login Request Results * * @param principal * @param request * @param successful */ @Override public void monitorLogInAttemptRequest(Object principal, HttpServletRequest request, boolean successful) { if (log.isDebugEnabled()) { log.debug("Monitoring " + ((successful) ? "Successful" : "Unsuccessful") + " Login Attempt for Principal:[" + principal + "] for Request:[" + request.toString() + "]"); } // ***************************** // Ensure we have a Principal. if (principal == null) { return; } SecurityServiceMonitorObject securityServiceMonitorObject = null; if (securityServiceMonitorObjects.containsKey(principal.toString().toLowerCase())) { // ********************************** // Update Existing Monitor Object. securityServiceMonitorObject = securityServiceMonitorObjects.get(principal.toString().toLowerCase()); if (securityServiceMonitorObject != null) { securityServiceMonitorObject.setLocalAddress(request.getLocalAddr()); securityServiceMonitorObject.setLocalName(request.getLocalName()); securityServiceMonitorObject.setLocalPort(request.getLocalPort()); securityServiceMonitorObject.setRemoteAddress(request.getRemoteAddr()); securityServiceMonitorObject.setRemoteName(request.getRemoteHost()); securityServiceMonitorObject.setRemotePort(request.getRemotePort()); if (successful) { securityServiceMonitorObject.incrementSuccessfulAttempts(); securityServiceMonitorObject.setLastSuccessfulLogInAttempt(new Date()); } else { securityServiceMonitorObject.incrementUnsuccessfulAttempts(); securityServiceMonitorObject.setLastUnsuccessfulLogInAttempt(new Date()); } } } // ********************************** // Add a new Monitor Object for // future checks. if (securityServiceMonitorObject == null) { securityServiceMonitorObject = new SecurityServiceMonitorObject(principal, request.getContextPath(), request.getLocalAddr(), request.getLocalName(), request.getLocalPort(), request.getRemoteAddr(), request.getRemoteHost(), request.getRemotePort(), successful); } securityServiceMonitorObjects.put(principal.toString().toLowerCase(), securityServiceMonitorObject); } /** * Report Log-In Attempts for Principal * * @param principal * @return SecurityServiceMonitorObject -- null if nothing contained for specified Principal */ public SecurityServiceMonitorObject reportLogInAttempts(Object principal) { return securityServiceMonitorObjects.get(principal.toString().toLowerCase()); } /** * Is Login Allowed? * <p/> * Will determines if a Login is even allowed, if the account is in a lock-out mode due * to consistent failed entry attempts * then this method will return false, otherwise true. * * @param principal * @param request * @return boolean indicating if login attempt allowed at this time or not. */ @Override public boolean isLoginAttemptAllowed(String principal, HttpServletRequest request) { if (!this.checkFailedLoginAttempts) { return true; } SecurityServiceMonitorObject securityServiceMonitorObject = this.reportLogInAttempts(principal); if (securityServiceMonitorObject == null) { return true; } // ************************************************** // We have to determine if the current login attempt // can be granted or denied based upon the number // of times a log-in has been attempted within a // determined time frame or window. // if (securityServiceMonitorObject.getUnsuccessfulAttemptsInSequence() < this.maxFailedLoginAttempts) { return true; } else if (securityServiceMonitorObject.getLastUnsuccessfulLogInAttempt() == null) { return true; } if (log.isDebugEnabled()) { log.debug("Checking Login Attempts for:[" + securityServiceMonitorObject.toString() + "]"); } // **************************************** // Determine if the last attempt has been // within the lock-out window? // calendar Calendar window_begin = Calendar.getInstance(); window_begin.setTime(securityServiceMonitorObject.getLastUnsuccessfulLogInAttempt()); Calendar window_end = Calendar.getInstance(); if ((window_end.getTimeInMillis() - window_begin.getTimeInMillis()) < (this.failedLoginSuspendedTimeout * 1000)) { log.warn("Locking out Principal:[" + principal + "], for at least " + this.failedLoginSuspendedTimeout + " seconds, due to Excessive Failed Log-In Attempts!"); return false; } else { return true; } } /** * Get the number of Login attempts for this instance. * * @param principal * @return long */ @Override public long getPrincipalAttempts(String principal) { SecurityServiceMonitorObject securityServiceMonitorObject = this.reportLogInAttempts(principal); if (securityServiceMonitorObject == null) { return 0; } return securityServiceMonitorObject.getTotalAttempts(); } /** * Get the number of Successful Login attempts for this instance. * * @param principal * @return long */ @Override public long getPrincipalSuccessfulAttempts(String principal) { SecurityServiceMonitorObject securityServiceMonitorObject = this.reportLogInAttempts(principal); if (securityServiceMonitorObject == null) { return 0; } return securityServiceMonitorObject.getSuccessfulAttemptsInSequence(); } /** * Get the number of UnSuccessful Login attempts for this instance. * * @param principal * @return long */ @Override public long getPrincipalUnsuccessfulAttempts(String principal) { SecurityServiceMonitorObject securityServiceMonitorObject = this.reportLogInAttempts(principal); if (securityServiceMonitorObject == null) { return 0; } return securityServiceMonitorObject.getUnsuccessfulAttemptsInSequence(); } /** * Clear Monitoring Object by principal. * * @param principal */ @Override public void clearPrincipal(String principal) { this.securityServiceMonitorObjects.remove(principal.toLowerCase()); } /** * Clear All Monitoring Objects. */ @Override public void clearAll() { this.securityServiceMonitorObjects.clear(); } }