From f91b19afcb1f6fe97b46aa694049db0a98d166a9 Mon Sep 17 00:00:00 2001
From: Mike McKay <mfmckay@wustl.edu>
Date: Mon, 30 May 2016 15:29:22 -0500
Subject: [PATCH] A bunch of fixes to get password expiration working.

---
 .../xnat/configuration/SchedulerConfig.java   |  1 +
 .../PasswordExpirationHandlerMethod.java      | 53 +++++++++++++++++
 .../security/XnatExpiredPasswordFilter.java   | 58 ++++++++++++++-----
 .../xnat/spawner/site-admin-elements.yaml     | 10 ++--
 .../config/site/siteConfiguration.properties  |  2 +-
 5 files changed, 103 insertions(+), 21 deletions(-)
 create mode 100644 src/main/java/org/nrg/xnat/event/listeners/methods/PasswordExpirationHandlerMethod.java

diff --git a/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java b/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java
index f69f7691..efe99510 100644
--- a/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java
+++ b/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java
@@ -54,6 +54,7 @@ public class SchedulerConfig implements SchedulingConfigurer {
         _eventService.triggerEvent(new PreferenceEvent("host", String.valueOf(XDAT.getNotificationsPreferences().getHostname())));
         _eventService.triggerEvent(new PreferenceEvent("requireLogin", String.valueOf(XDAT.getSiteConfigPreferences().getRequireLogin())));
         _eventService.triggerEvent(new PreferenceEvent("security.channel", String.valueOf(XDAT.getSiteConfigPreferences().getSecurityChannel())));
+        _eventService.triggerEvent(new PreferenceEvent("passwordExpirationType", String.valueOf(XDAT.getSiteConfigPreferences().getPasswordExpirationType())));
         for (final TriggerTask triggerTask : _triggerTasks) {
             taskRegistrar.addTriggerTask(triggerTask);
         }
diff --git a/src/main/java/org/nrg/xnat/event/listeners/methods/PasswordExpirationHandlerMethod.java b/src/main/java/org/nrg/xnat/event/listeners/methods/PasswordExpirationHandlerMethod.java
new file mode 100644
index 00000000..cf5bafca
--- /dev/null
+++ b/src/main/java/org/nrg/xnat/event/listeners/methods/PasswordExpirationHandlerMethod.java
@@ -0,0 +1,53 @@
+package org.nrg.xnat.event.listeners.methods;
+
+import com.google.common.collect.ImmutableList;
+import org.nrg.xnat.security.XnatExpiredPasswordFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+@Component
+public class PasswordExpirationHandlerMethod extends AbstractSiteConfigPreferenceHandlerMethod {
+    @Override
+    public List<String> getHandledPreferences() {
+        return PREFERENCES;
+    }
+
+    @Override
+    public void handlePreferences(final Map<String, String> values) {
+        if (!Collections.disjoint(PREFERENCES, values.keySet())) {
+            updatePasswordExpiration();
+        }
+    }
+
+    @Override
+    public void handlePreference(final String preference, final String value) {
+        if (PREFERENCES.contains(preference)) {
+            updatePasswordExpiration();
+        }
+    }
+
+    private void updatePasswordExpiration() {
+        _filter.refreshFromSiteConfig();
+    }
+
+    private static final Logger       _log        = LoggerFactory.getLogger(PasswordExpirationHandlerMethod.class);
+    private static final List<String> PREFERENCES = ImmutableList.copyOf(Arrays.asList("passwordExpirationType", "passwordExpirationInterval", "passwordExpirationDate"));
+
+    @Autowired
+    @Lazy
+    private JdbcTemplate _template;
+
+    @Autowired
+    @Qualifier("expiredPasswordFilter")
+    private XnatExpiredPasswordFilter _filter;
+}
diff --git a/src/main/java/org/nrg/xnat/security/XnatExpiredPasswordFilter.java b/src/main/java/org/nrg/xnat/security/XnatExpiredPasswordFilter.java
index 86f24801..90847a01 100644
--- a/src/main/java/org/nrg/xnat/security/XnatExpiredPasswordFilter.java
+++ b/src/main/java/org/nrg/xnat/security/XnatExpiredPasswordFilter.java
@@ -184,7 +184,7 @@ public class XnatExpiredPasswordFilter extends GenericFilterBean {
                     } else if (user.isEnabled()) {
                         boolean isExpired = checkForExpiredPassword(user);
 
-                        if ((!isUserNonExpiring(user) && isExpired) || (_preferences.getRequireSaltedPasswords() && user.getSalt() == null)) {
+                        if ((!isUserNonExpiring(user) && isExpired) || (_initializerPreferences.getRequireSaltedPasswords() && user.getSalt() == null)) {
                             request.getSession().setAttribute("expired", isExpired);
                             response.sendRedirect(TurbineUtils.GetFullServerPath() + changePasswordPath);
                         } else {
@@ -264,7 +264,7 @@ public class XnatExpiredPasswordFilter extends GenericFilterBean {
                 return false;
             }
             if (isPasswordExpirationInterval()) {
-                List<Boolean> expired = (new JdbcTemplate(_dataSource)).query("SELECT ((now()-password_updated)> (Interval '" + passwordExpirationSetting + " days')) AS expired FROM xhbm_xdat_user_auth WHERE auth_user = ? AND auth_method = 'localdb'", new String[] {username}, new RowMapper<Boolean>() {
+                List<Boolean> expired = (new JdbcTemplate(_dataSource)).query("SELECT ((now()-password_updated)> (Interval '" + passwordExpirationSetting + "')) AS expired FROM xhbm_xdat_user_auth WHERE auth_user = ? AND auth_method = 'localdb'", new String[] {username}, new RowMapper<Boolean>() {
                     public Boolean mapRow(ResultSet rs, int rowNum) throws SQLException {
                         return rs.getBoolean(1);
                     }
@@ -288,20 +288,39 @@ public class XnatExpiredPasswordFilter extends GenericFilterBean {
         if (!passwordExpirationDirtied) {
             return passwordExpirationDisabled;
         }
-        final String type = _preferences.getPasswordExpirationType();
-        if (StringUtils.isBlank(type)) {
-            passwordExpirationDisabled = true;
-        } else if (type.equals("Interval")) {
-            passwordExpirationInterval = true;
-            passwordExpirationSetting = Integer.toString(_preferences.getPasswordExpirationInterval());
-            passwordExpirationDisabled = passwordExpirationSetting.equals("0");
-        } else if (type.equals("Date")) {
-            passwordExpirationInterval = false;
-            passwordExpirationSetting = Long.toString(_preferences.getPasswordExpirationDate().getTime());
-            passwordExpirationDisabled = passwordExpirationSetting.equals("0");
-        } else {
-            passwordExpirationDisabled = true;
+        if(useSiteConfigPrefs){
+            final String type = XDAT.getSiteConfigPreferences().getPasswordExpirationType();
+            if (StringUtils.isBlank(type)) {
+                passwordExpirationDisabled = true;
+            } else if (type.equals("Interval")) {
+                passwordExpirationInterval = true;
+                passwordExpirationSetting = XDAT.getSiteConfigPreferences().getPasswordExpirationInterval();
+                passwordExpirationDisabled = passwordExpirationSetting.equals("0");
+            } else if (type.equals("Date")) {
+                passwordExpirationInterval = false;
+                passwordExpirationSetting = Long.toString((new Date(XDAT.getSiteConfigPreferences().getPasswordExpirationDate())).getTime());
+                passwordExpirationDisabled = passwordExpirationSetting.equals("0");
+            } else {
+                passwordExpirationDisabled = true;
+            }
+        }
+        else{
+            final String type = _initializerPreferences.getPasswordExpirationType();
+            if (StringUtils.isBlank(type)) {
+                passwordExpirationDisabled = true;
+            } else if (type.equals("Interval")) {
+                passwordExpirationInterval = true;
+                passwordExpirationSetting = _initializerPreferences.getPasswordExpirationInterval();
+                passwordExpirationDisabled = StringUtils.equals(passwordExpirationSetting,"0");
+            } else if (type.equals("Date")) {
+                passwordExpirationInterval = false;
+                passwordExpirationSetting = Long.toString((new Date(_initializerPreferences.getPasswordExpirationDate())).getTime());
+                passwordExpirationDisabled = StringUtils.equals(passwordExpirationSetting,"0");
+            } else {
+                passwordExpirationDisabled = true;
+            }
         }
+
         passwordExpirationDirtied = false;
         return passwordExpirationDisabled;
     }
@@ -337,9 +356,16 @@ public class XnatExpiredPasswordFilter extends GenericFilterBean {
         return _aliasTokenService;
     }
 
+    public void refreshFromSiteConfig(){
+        useSiteConfigPrefs = true;
+        passwordExpirationDirtied = true;
+    }
+
+    private boolean useSiteConfigPrefs = false;
+
     @Autowired
     @Lazy
-    private InitializerSiteConfiguration _preferences;
+    private InitializerSiteConfiguration _initializerPreferences;
 
     @Autowired
     @Lazy
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 8f4b0533..2b5f0b38 100755
--- a/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
+++ b/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
@@ -398,17 +398,19 @@ passwords:
                     label: Date
                     value: Date
         passwordExpirationInterval:
-            kind: panel.input.number
+            kind: panel.input.text
             id: passwordExpirationInterval
             name: passwordExpirationInterval
             label: Password Expiration (Interval)
-            description: "-1 to disable"
+            description: >
+              Interval of time after which unchanged passwords expire and users have to change them. Enter "0" to disable. Uses
+              <a target="_blank" href="http://www.postgresql.org/docs/9.0/static/functions-datetime.html">PostgreSQL interval notation</a>
         passwordExpirationDate:
             kind: panel.input.text
             id: passwordExpirationDate
             name: passwordExpirationDate
             label: Password Expiration (Date)
-            description: Dates must be formatted MM/DD/YYYY
+            description: Expire passwords that were last changed before this date and require those users to change them. Enter "0" to disable. Dates must be formatted MM/DD/YYYY
         passwordReuseRestriction:
             kind: panel.select.single
             id: passwordReuseRestriction
@@ -425,7 +427,7 @@ passwords:
             kind: panel.input.text
             id: passwordHistoryDuration
             name: passwordHistoryDuration
-            label: Password History (Duration)
+            label: Password History
             description: >
               Interval for which users cannot reuse an old password of theirs. Uses
               <a target="_blank" href="http://www.postgresql.org/docs/9.0/static/functions-datetime.html">PostgreSQL interval notation</a>
diff --git a/src/main/resources/config/site/siteConfiguration.properties b/src/main/resources/config/site/siteConfiguration.properties
index 30362515..28f57b9d 100644
--- a/src/main/resources/config/site/siteConfiguration.properties
+++ b/src/main/resources/config/site/siteConfiguration.properties
@@ -48,6 +48,6 @@ scanTypeMapping=true
 
 passwordExpirationType=Interval
 # Interval, in days, for expiring unchanged passwords (0 disables feature). Uses PostgreSQL interval notation: http://www.postgresql.org/docs/9.0/static/functions-datetime.html
-passwordExpirationInterval=365
+passwordExpirationInterval=1 year
 # Date for expiring passwords. Generally null by default.
 passwordExpirationDate=
\ No newline at end of file
-- 
GitLab