From 3735b60d10738e6d59b31fa446af7efe282376a2 Mon Sep 17 00:00:00 2001
From: Mike McKay <mfmckay@wustl.edu>
Date: Tue, 24 May 2016 17:10:00 -0500
Subject: [PATCH] Added preference for how often to check if locked out users
 should be let back in. Made how to specify time intervals consistent within
 the User Logins and Session Controls section by having Failed Logins Lockout
 Duration and User Inactivity Lockout both be entered in Intervals.

---
 .../xnat/configuration/SchedulerConfig.java   |  9 ++---
 .../SiteConfigPreferenceHandler.java          | 26 +++++++++++++-
 .../security/XnatSessionEventPublisher.java   |  8 ++++-
 .../xnat/spawner/site-admin-elements.yaml     | 35 ++++++++++++++-----
 4 files changed, 63 insertions(+), 15 deletions(-)

diff --git a/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java b/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java
index bab832ad..2fc83765 100644
--- a/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java
+++ b/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java
@@ -52,10 +52,10 @@ public class SchedulerConfig implements SchedulingConfigurer {
 //        }
 //    }
 
-    @Bean
-    public TriggerTask resetFailedLogins() throws SiteConfigurationException {
-        return new TriggerTask(new ResetFailedLogins(_template, _preferences.getMaxFailedLoginsLockoutDuration()), new PeriodicTrigger(900000));
-    }
+//    @Bean
+//    public TriggerTask resetFailedLogins() throws SiteConfigurationException {
+//        return new TriggerTask(new ResetFailedLogins(_template, _preferences.getMaxFailedLoginsLockoutDuration()), new PeriodicTrigger(900000));
+//    }
 
     @Bean
     public TriggerTask resetEmailRequests() {
@@ -106,6 +106,7 @@ public class SchedulerConfig implements SchedulingConfigurer {
 //        taskRegistrar.addTriggerTask(rebuildSessionXmls());
         XDAT.getContextService().getBean(NrgEventService.class).triggerEvent(new SiteConfigPreferenceEvent("aliasTokenTimeout", String.valueOf(XDAT.getSiteConfigPreferences().getAliasTokenTimeout())));
         XDAT.getContextService().getBean(NrgEventService.class).triggerEvent(new SiteConfigPreferenceEvent("inactivityBeforeLockout", String.valueOf(XDAT.getSiteConfigPreferences().getInactivityBeforeLockout())));
+        XDAT.getContextService().getBean(NrgEventService.class).triggerEvent(new SiteConfigPreferenceEvent("maxFailedLoginsLockoutDuration", String.valueOf(XDAT.getSiteConfigPreferences().getMaxFailedLoginsLockoutDuration())));
         for (final TriggerTask triggerTask : _triggerTasks) {
             taskRegistrar.addTriggerTask(triggerTask);
         }
diff --git a/src/main/java/org/nrg/xnat/event/listeners/SiteConfigPreferenceHandler.java b/src/main/java/org/nrg/xnat/event/listeners/SiteConfigPreferenceHandler.java
index 82b939c3..9bfb3768 100644
--- a/src/main/java/org/nrg/xnat/event/listeners/SiteConfigPreferenceHandler.java
+++ b/src/main/java/org/nrg/xnat/event/listeners/SiteConfigPreferenceHandler.java
@@ -8,6 +8,7 @@ import org.nrg.xdat.XDAT;
 import org.nrg.xdat.preferences.SiteConfigPreferenceEvent;
 import org.nrg.xdat.preferences.SiteConfigPreferences;
 import org.nrg.xnat.security.DisableInactiveUsers;
+import org.nrg.xnat.security.ResetFailedLogins;
 import org.nrg.xnat.security.alias.ClearExpiredAliasTokens;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
@@ -56,6 +57,12 @@ public class SiteConfigPreferenceHandler implements Consumer<Event<SiteConfigPre
 				else if (StringUtils.equals(field.getFieldName(), "inactivityBeforeLockoutSchedule")) {
 					updateInactivityBeforeLockout(e);
 				}
+				else if (StringUtils.equals(field.getFieldName(), "maxFailedLoginsLockoutDuration")) {
+					updateResetFailedLogins(e);
+				}
+				else if (StringUtils.equals(field.getFieldName(), "resetFailedLoginsSchedule")) {
+					updateResetFailedLogins(e);
+				}
 			}
 		}
     }
@@ -85,7 +92,23 @@ public class SiteConfigPreferenceHandler implements Consumer<Event<SiteConfigPre
 				temp.cancel(false);
 			}
 
-			scheduledInactivityBeforeLockout.add(XDAT.getContextService().getBeansOfType(ThreadPoolTaskScheduler.class).get("taskScheduler").schedule(new DisableInactiveUsers(XDAT.getSiteConfigPreferences().getInactivityBeforeLockout(),(int) SiteConfigPreferences.convertPGIntervalToSeconds(XDAT.getSiteConfigPreferences().getMaxFailedLoginsLockoutDuration())),new CronTrigger(XDAT.getSiteConfigPreferences().getInactivityBeforeLockoutSchedule())));
+			scheduledInactivityBeforeLockout.add(XDAT.getContextService().getBeansOfType(ThreadPoolTaskScheduler.class).get("taskScheduler").schedule(new DisableInactiveUsers((new Long(SiteConfigPreferences.convertPGIntervalToSeconds(XDAT.getSiteConfigPreferences().getInactivityBeforeLockout()))).intValue(),(new Long(SiteConfigPreferences.convertPGIntervalToSeconds(XDAT.getSiteConfigPreferences().getMaxFailedLoginsLockoutDuration()))).intValue()),new CronTrigger(XDAT.getSiteConfigPreferences().getInactivityBeforeLockoutSchedule())));
+
+		} catch (Exception e1) {
+			_log.error("", e1);
+		}
+	}
+
+	private void updateResetFailedLogins(SiteConfigPreferenceEvent e){
+		try {
+			XDAT.getContextService().getBeansOfType(ThreadPoolTaskScheduler.class).get("taskScheduler").getScheduledThreadPoolExecutor().setRemoveOnCancelPolicy(true);
+			Iterator<Runnable> iter = XDAT.getContextService().getBeansOfType(ThreadPoolTaskScheduler.class).get("taskScheduler").getScheduledThreadPoolExecutor().getQueue().iterator();
+
+			for(ScheduledFuture temp: scheduledResetFailedLogins){
+				temp.cancel(false);
+			}
+
+			scheduledResetFailedLogins.add(XDAT.getContextService().getBeansOfType(ThreadPoolTaskScheduler.class).get("taskScheduler").schedule(new ResetFailedLogins(_template,XDAT.getSiteConfigPreferences().getMaxFailedLoginsLockoutDuration()),new CronTrigger(XDAT.getSiteConfigPreferences().getResetFailedLoginsSchedule())));
 
 		} catch (Exception e1) {
 			_log.error("", e1);
@@ -100,4 +123,5 @@ public class SiteConfigPreferenceHandler implements Consumer<Event<SiteConfigPre
 
 	private ArrayList<ScheduledFuture> scheduledAliasTokenTimeouts = new ArrayList<ScheduledFuture>();
 	private ArrayList<ScheduledFuture> scheduledInactivityBeforeLockout = new ArrayList<ScheduledFuture>();
+	private ArrayList<ScheduledFuture> scheduledResetFailedLogins = new ArrayList<ScheduledFuture>();
 }
diff --git a/src/main/java/org/nrg/xnat/security/XnatSessionEventPublisher.java b/src/main/java/org/nrg/xnat/security/XnatSessionEventPublisher.java
index 437c17d7..5fec5247 100644
--- a/src/main/java/org/nrg/xnat/security/XnatSessionEventPublisher.java
+++ b/src/main/java/org/nrg/xnat/security/XnatSessionEventPublisher.java
@@ -11,6 +11,7 @@
 package org.nrg.xnat.security;
 
 import org.nrg.xdat.XDAT;
+import org.nrg.xdat.preferences.SiteConfigPreferences;
 import org.nrg.xft.security.UserI;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -31,6 +32,7 @@ import javax.servlet.ServletContextListener;
 import javax.servlet.http.HttpSession;
 import javax.servlet.http.HttpSessionEvent;
 import javax.servlet.http.HttpSessionListener;
+import java.sql.SQLException;
 import java.sql.Timestamp;
 import java.util.Calendar;
 import java.util.Date;
@@ -53,7 +55,11 @@ public class XnatSessionEventPublisher implements HttpSessionListener, ServletCo
         }
 
         session.setAttribute("XNAT_CSRF", UUID.randomUUID().toString());
-        session.setMaxInactiveInterval(XDAT.getSiteConfigPreferences().getSessionTimeout()*60);//Preference is in minutes and setMaxInactiveInterval wants seconds.
+        try {
+            session.setMaxInactiveInterval((new Long(SiteConfigPreferences.convertPGIntervalToSeconds(XDAT.getSiteConfigPreferences().getSessionTimeout()))).intValue());//Preference is in PG Interval and setMaxInactiveInterval wants seconds.
+        } catch (SQLException e1) {
+            _log.error("" + e);
+        }
         getContext(session.getServletContext()).publishEvent(e);
     }
 
diff --git a/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml b/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
index f76a2c02..35d6213d 100644
--- a/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
+++ b/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
@@ -219,24 +219,28 @@ userLoginsSessionControls:
     contentType: json
     contents:
         sessionTimeout:
-            kind: panel.input.number
+            kind: panel.input.text
             id: sessionTimeout
             name: sessionTimeout
             label: Session Timeout
-            description: "Interval for timing out alias tokens"
+            description: >
+              Interval for timing out user sessions. Uses
+              <a target="_blank" href="http://www.postgresql.org/docs/9.0/static/functions-datetime.html">PostgreSQL interval notation</a>
         aliasTokenTimeout:
             kind: panel.input.text
             id: aliasTokenTimeout
             name: aliasTokenTimeout
             label: Alias Token Timeout
-            description: Interval for timing out alias tokens. Uses
+            description: >
+              Interval for timing out alias tokens. Uses
               <a target="_blank" href="http://www.postgresql.org/docs/9.0/static/functions-datetime.html">PostgreSQL interval notation</a>
         aliasTokenTimeoutSchedule:
             kind: panel.input.text
             id: aliasTokenTimeoutSchedule
             name: aliasTokenTimeoutSchedule
             label: Alias Token Timeout Schedule
-            description: How often to check alias tokens for timeout (0 0 * * * * means it runs every hour). Uses
+            description: >
+              How often to check alias tokens for timeout (0 0 * * * * means it runs every hour). Uses
               <a target="_blank" href="http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/support/CronSequenceGenerator.html">Cron notation</a>
         sessionTimeoutMessage:
             kind: panel.textarea
@@ -269,23 +273,36 @@ userLoginsSessionControls:
             label: Maximum Failed Logins
             description: Number of failed login attempts before accounts are temporarily locked. (-1 disables feature)
         failedLoginLockoutDuration:
-            kind: panel.input.number
+            kind: panel.input.text
             id: failedLoginLockoutDuration
             name: maxFailedLoginsLockoutDuration
             label: Failed Logins Lockout Duration
-            description: Number of milliseconds to lock user accounts that have exceeded the max_failed_logins count. Select (3600000 for 1 hour, 86400000 for 24 hours)
+            description: >
+              Interval of time to lock user accounts that have exceeded the max_failed_logins count. Uses
+              <a target="_blank" href="http://www.postgresql.org/docs/9.0/static/functions-datetime.html">PostgreSQL interval notation</a>
+        resetFailedLoginsSchedule:
+            kind: panel.input.text
+            id: resetFailedLoginsSchedule
+            name: resetFailedLoginsSchedule
+            label: Reset Failed Logins Schedule
+            description: >
+              How often to check if the Failed Logins Lockout Duration time has expired so locked out users can be allowed to log in again (0 0 * * * * means it runs every hour). Uses
+              <a target="_blank" href="http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/support/CronSequenceGenerator.html">Cron notation</a>
         userInactivityLockout:
-            kind: panel.input.number
+            kind: panel.input.text
             id: userInactivityLockout
             name: inactivityBeforeLockout
             label: User Inactivity Lockout
-            description: Number of seconds of inactivity before an account is disabled (31556926 for 1 year)
+            description: >
+              Interval of inactivity before a user account is disabled. Uses
+              <a target="_blank" href="http://www.postgresql.org/docs/9.0/static/functions-datetime.html">PostgreSQL interval notation</a>
         inactivityBeforeLockoutSchedule:
             kind: panel.input.text
             id: inactivityBeforeLockoutSchedule
             name: inactivityBeforeLockoutSchedule
             label: Inactivity Lockout Schedule
-            description: How often to check user accounts for inactivity (0 0 1 * * * means it runs at 1AM every day). Uses
+            description: >
+              How often to check user accounts for inactivity (0 0 1 * * * means it runs at 1AM every day). Uses
               <a target="_blank" href="http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/support/CronSequenceGenerator.html">Cron notation</a>
 
 
-- 
GitLab