You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by jm...@apache.org on 2017/01/05 21:50:23 UTC

[1/7] incubator-guacamole-client git commit: GUACAMOLE-36: Define historical password records. Use password records for password age comparisons.

Repository: incubator-guacamole-client
Updated Branches:
  refs/heads/master 102d52f0f -> d45580f22


GUACAMOLE-36: Define historical password records. Use password records for password age comparisons.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/3c718f27
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/3c718f27
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/3c718f27

Branch: refs/heads/master
Commit: 3c718f27bffb3f28f83b3f632b7b7288c9a13302
Parents: 102d52f
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Aug 22 15:43:52 2016 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Thu Jan 5 10:35:19 2017 -0800

----------------------------------------------------------------------
 .../jdbc/security/PasswordPolicyService.java    |   8 +-
 .../guacamole/auth/jdbc/user/ModeledUser.java   |  33 ++--
 .../auth/jdbc/user/PasswordRecordModel.java     | 156 +++++++++++++++++++
 3 files changed, 182 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/3c718f27/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
index a47c038..dfa980c 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
@@ -26,6 +26,7 @@ import java.util.regex.Pattern;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
 import org.apache.guacamole.auth.jdbc.user.ModeledUser;
+import org.apache.guacamole.auth.jdbc.user.PasswordRecordModel;
 
 /**
  * Service which verifies compliance with the password policy configured via
@@ -161,9 +162,14 @@ public class PasswordPolicyService {
      */
     private long getPasswordAge(ModeledUser user) {
 
+        // If no password was set, then no time has elapsed
+        PasswordRecordModel previousPassword = user.getPreviousPassword();
+        if (previousPassword == null)
+            return 0;
+
         // Pull both current time and the time the password was last reset
         long currentTime = System.currentTimeMillis();
-        long lastResetTime = user.getPreviousPasswordDate().getTime();
+        long lastResetTime = previousPassword.getPasswordDate().getTime();
 
         // Calculate the number of days elapsed since the password was last reset
         return TimeUnit.DAYS.convert(currentTime - lastResetTime, TimeUnit.MILLISECONDS);

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/3c718f27/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java
index 2f1e583..18a13ec 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java
@@ -189,10 +189,10 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
     private String password = null;
 
     /**
-     * The time and date that this user's password was previously set (prior to
-     * being queried). If the user is new, this will be null.
+     * The data associated with this user's password at the time this user was
+     * queried. If the user is new, this will be null.
      */
-    private Timestamp previousPasswordDate = null;
+    private PasswordRecordModel previousPassword = null;
     
     /**
      * Creates a new, empty ModeledUser.
@@ -202,8 +202,13 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
 
     @Override
     public void setModel(UserModel model) {
+
         super.setModel(model);
-        this.previousPasswordDate = model.getPasswordDate();
+
+        // Store previous password, if any
+        if (model.getPasswordHash() != null)
+            this.previousPassword = new PasswordRecordModel(model);
+
     }
 
     @Override
@@ -240,19 +245,19 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
     }
 
     /**
-     * Returns the time and date that this user's password was previously set.
-     * If the user is new, this will be null. Unlike getPasswordDate() of
-     * UserModel (which is updated automatically along with the password salt
-     * and hash whenever setPassword() is invoked), this value is unaffected by
-     * calls to setPassword(), and will always be the value stored in the
-     * database at the time this user was queried.
+     * Returns the data associated with this user's previous password as a
+     * password record. If the user is new, this will be null. Unlike the other
+     * password-related functions of UserModel, this data returned by this
+     * function is historical and is unaffected by calls to setPassword(). It
+     * will always return the values stored in the database at the time this
+     * user was queried.
      *
      * @return
-     *     The time and date that this user's password was previously set, or
-     *     null if the user is new.
+     *     The data associated with this user's previous password, or null if
+     *     the user is new.
      */
-    public Timestamp getPreviousPasswordDate() {
-        return previousPasswordDate;
+    public PasswordRecordModel getPreviousPassword() {
+        return previousPassword;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/3c718f27/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PasswordRecordModel.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PasswordRecordModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PasswordRecordModel.java
new file mode 100644
index 0000000..4b34f80
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PasswordRecordModel.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.auth.jdbc.user;
+
+import java.sql.Timestamp;
+
+/**
+ * A single password record representing a previous password of a particular
+ * user, along with the time/date that password was set.
+ *
+ * @author Michael Jumper
+ */
+public class PasswordRecordModel {
+
+    /**
+     * The database ID of the user associated with this password record.
+     */
+    private Integer userID;
+
+    /**
+     * The hash of the password and salt.
+     */
+    private byte[] passwordHash;
+
+    /**
+     * The random salt that was appended to the password prior to hashing.
+     */
+    private byte[] passwordSalt;
+
+    /**
+     * The date and time when this password was first set for the associated
+     * user.
+     */
+    private Timestamp passwordDate;
+
+    /**
+     * Creates a new, empty PasswordRecordModel.
+     */
+    public PasswordRecordModel() {
+    }
+
+    /**
+     * Creates a new PasswordRecordModel associated with the given user and
+     * populated with that user's password hash and salt.
+     *
+     * @param user
+     *     The user to associate with this PasswordRecordModel.
+     */
+    public PasswordRecordModel(UserModel user) {
+        this.userID = user.getObjectID();
+        this.passwordHash = user.getPasswordHash();
+        this.passwordSalt = user.getPasswordSalt();
+        this.passwordDate = user.getPasswordDate();
+    }
+
+    /**
+     * Returns the database ID of the user associated with this password
+     * record.
+     *
+     * @return
+     *     The database ID of the user associated with this password record.
+     */
+    public Integer getUserID() {
+        return userID;
+    }
+
+    /**
+     * Sets the database ID of the user associated with this password record.
+     *
+     * @param userID
+     *     The database ID of the user to associate with this password
+     *     record.
+     */
+    public void setUserID(Integer userID) {
+        this.userID = userID;
+    }
+
+    /**
+     * Returns the hash of the password and password salt.
+     *
+     * @return
+     *     The hash of the password and password salt.
+     */
+    public byte[] getPasswordHash() {
+        return passwordHash;
+    }
+
+    /**
+     * Sets the hash of the password and password salt.
+     *
+     * @param passwordHash
+     *     The hash of the password and password salt.
+     */
+    public void setPasswordHash(byte[] passwordHash) {
+        this.passwordHash = passwordHash;
+    }
+
+    /**
+     * Returns the random salt that was used when generating the password hash.
+     *
+     * @return
+     *     The random salt that was used when generating the password hash.
+     */
+    public byte[] getPasswordSalt() {
+        return passwordSalt;
+    }
+
+    /**
+     * Sets the random salt that was used when generating the password hash.
+     *
+     * @param passwordSalt
+     *     The random salt used when generating the password hash.
+     */
+    public void setPasswordSalt(byte[] passwordSalt) {
+        this.passwordSalt = passwordSalt;
+    }
+
+    /**
+     * Returns the date that this password was first set for the associated
+     * user.
+     *
+     * @return
+     *     The date that this password was first set for the associated user.
+     */
+    public Timestamp getPasswordDate() {
+        return passwordDate;
+    }
+
+    /**
+     * Sets the date that this password was first set for the associated user.
+     *
+     * @param passwordDate
+     *     The date that this password was first set for the associated user.
+     */
+    public void setPasswordDate(Timestamp passwordDate) {
+        this.passwordDate = passwordDate;
+    }
+
+}


[4/7] incubator-guacamole-client git commit: GUACAMOLE-36: Verify new passwords against history.

Posted by jm...@apache.org.
GUACAMOLE-36: Verify new passwords against history.

Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/4a1ae7f2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/4a1ae7f2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/4a1ae7f2

Branch: refs/heads/master
Commit: 4a1ae7f29235f6db38befa01e7dac4c5cccfce06
Parents: a943077
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Aug 22 23:11:48 2016 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Thu Jan 5 13:37:13 2017 -0800

----------------------------------------------------------------------
 .../jdbc/security/PasswordPolicyService.java    | 57 ++++++++++++++++++++
 .../jdbc/security/PasswordReusedException.java  | 52 ++++++++++++++++++
 .../src/main/resources/translations/en.json     |  1 +
 3 files changed, 110 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/4a1ae7f2/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
index 2b6c74c..d6a9fe5 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
@@ -20,6 +20,8 @@
 package org.apache.guacamole.auth.jdbc.security;
 
 import com.google.inject.Inject;
+import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -50,6 +52,12 @@ public class PasswordPolicyService {
     private PasswordRecordMapper passwordRecordMapper;
 
     /**
+     * Service for hashing passwords.
+     */
+    @Inject
+    private PasswordEncryptionService encryptionService;
+
+    /**
      * Regular expression which matches only if the string contains at least one
      * lowercase character.
      */
@@ -106,6 +114,49 @@ public class PasswordPolicyService {
     }
 
     /**
+     * Returns whether the given password matches any of the user's previous
+     * passwords. Regardless of the value specified here, the maximum number of
+     * passwords involved in this check depends on how many previous passwords
+     * were actually recorded, which depends on the password policy.
+     *
+     * @param password
+     *     The password to check.
+     *
+     * @param username
+     *     The username of the user whose history should be compared against
+     *     the given password.
+     *
+     * @param historySize
+     *     The maximum number of history records to compare the password
+     *     against.
+     *
+     * @return
+     *     true if the given password matches any of the user's previous
+     *     passwords, up to the specified limit, false otherwise.
+     */
+    private boolean matchesPreviousPasswords(String password, String username,
+            int historySize) {
+
+        // No need to compare if no history is relevant
+        if (historySize <= 0)
+            return false;
+
+        // Check password against all recorded hashes
+        List<PasswordRecordModel> history = passwordRecordMapper.select(username, historySize);
+        for (PasswordRecordModel record : history) {
+
+            byte[] hash = encryptionService.createPasswordHash(password, record.getPasswordSalt());
+            if (Arrays.equals(hash, record.getPasswordHash()))
+                return true;
+            
+        }
+
+        // No passwords match
+        return false;
+        
+    }
+
+    /**
      * Verifies that the given new password complies with the password policy
      * configured within guacamole.properties, throwing a GuacamoleException if
      * the policy is violated in any way.
@@ -152,6 +203,12 @@ public class PasswordPolicyService {
             throw new PasswordRequiresSymbolException(
                     "Passwords must contain at least one non-alphanumeric character.");
 
+        // Prohibit password reuse
+        int historySize = policy.getHistorySize();
+        if (matchesPreviousPasswords(password, username, historySize))
+            throw new PasswordReusedException(
+                    "Password matches a previously-used password.", historySize);
+
         // Password passes all defined restrictions
 
     }

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/4a1ae7f2/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordReusedException.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordReusedException.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordReusedException.java
new file mode 100644
index 0000000..3e3ad9b
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordReusedException.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.auth.jdbc.security;
+
+import java.util.Collections;
+import org.apache.guacamole.language.TranslatableMessage;
+
+/**
+ * Thrown when an attempt is made to reuse a previous password, in violation of
+ * the defined password policy.
+ *
+ * @author Michael Jumper
+ */
+public class PasswordReusedException extends PasswordPolicyException {
+
+    /**
+     * Creates a new PasswordReusedException with the given human-readable
+     * message. The translatable message is already defined.
+     *
+     * @param message
+     *     A human-readable message describing the password policy violation
+     *     that occurred.
+     *
+     * @param historySize
+     *     The number of previous passwords which are remembered for each user,
+     *     and must not be reused.
+     */
+    public PasswordReusedException(String message, int historySize) {
+        super(message, new TranslatableMessage(
+            "PASSWORD_POLICY.ERROR_REUSED",
+            Collections.singletonMap("HISTORY_SIZE", historySize)
+        ));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/4a1ae7f2/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/resources/translations/en.json
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/resources/translations/en.json b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/resources/translations/en.json
index 95bb59b..78728c3 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/resources/translations/en.json
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/resources/translations/en.json
@@ -60,6 +60,7 @@
         "ERROR_REQUIRES_DIGIT"         : "Passwords must contain at least one digit.",
         "ERROR_REQUIRES_MULTIPLE_CASE" : "Passwords must contain both uppercase and lowercase characters.",
         "ERROR_REQUIRES_NON_ALNUM"     : "Passwords must contain at least one symbol.",
+        "ERROR_REUSED"                 : "This password has already been used. Please do not reuse any of the previous {HISTORY_SIZE} {HISTORY_SIZE, plural, one{password} other{passwords}}.",
         "ERROR_TOO_SHORT"              : "Passwords must be at least {LENGTH} {LENGTH, plural, one{character} other{characters}} long.",
         "ERROR_TOO_YOUNG"              : "The password for this account has already been reset. Please wait at least {WAIT} more {WAIT, plural, one{day} other{days}} before changing the password again."
 


[2/7] incubator-guacamole-client git commit: GUACAMOLE-36: Clarify function naming regarding a user's current (at time of query) password record.

Posted by jm...@apache.org.
GUACAMOLE-36: Clarify function naming regarding a user's current (at time of query) password record.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/52dda6b5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/52dda6b5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/52dda6b5

Branch: refs/heads/master
Commit: 52dda6b55f4538c40195abbef3d954dbd655eb3c
Parents: 4a1ae7f
Author: Michael Jumper <mj...@apache.org>
Authored: Thu Jan 5 13:36:58 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Thu Jan 5 13:37:13 2017 -0800

----------------------------------------------------------------------
 .../jdbc/security/PasswordPolicyService.java    | 13 ++++++------
 .../guacamole/auth/jdbc/user/ModeledUser.java   | 22 ++++++++++----------
 .../guacamole/auth/jdbc/user/UserService.java   |  2 +-
 3 files changed, 19 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/52dda6b5/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
index d6a9fe5..a9fbcf3 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
@@ -227,13 +227,13 @@ public class PasswordPolicyService {
     private long getPasswordAge(ModeledUser user) {
 
         // If no password was set, then no time has elapsed
-        PasswordRecordModel previousPassword = user.getPreviousPassword();
-        if (previousPassword == null)
+        PasswordRecordModel passwordRecord = user.getPasswordRecord();
+        if (passwordRecord == null)
             return 0;
 
         // Pull both current time and the time the password was last reset
         long currentTime = System.currentTimeMillis();
-        long lastResetTime = previousPassword.getPasswordDate().getTime();
+        long lastResetTime = passwordRecord.getPasswordDate().getTime();
 
         // Calculate the number of days elapsed since the password was last reset
         return TimeUnit.DAYS.convert(currentTime - lastResetTime, TimeUnit.MILLISECONDS);
@@ -306,12 +306,13 @@ public class PasswordPolicyService {
      * user is limited by the password policy.
      *
      * @param user
-     *     The user whose previous password should be recorded.
+     *     The user whose password should be recorded within the password
+     *     history.
      *
      * @throws GuacamoleException
      *     If the password policy cannot be parsed.
      */
-    public void recordPreviousPassword(ModeledUser user)
+    public void recordPassword(ModeledUser user)
             throws GuacamoleException {
 
         // Retrieve password policy from environment
@@ -323,7 +324,7 @@ public class PasswordPolicyService {
             return;
         
         // Store previous password in history
-        passwordRecordMapper.insert(user.getPreviousPassword(), historySize);
+        passwordRecordMapper.insert(user.getPasswordRecord(), historySize);
 
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/52dda6b5/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java
index 18a13ec..d357003 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java
@@ -192,7 +192,7 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
      * The data associated with this user's password at the time this user was
      * queried. If the user is new, this will be null.
      */
-    private PasswordRecordModel previousPassword = null;
+    private PasswordRecordModel passwordRecord = null;
     
     /**
      * Creates a new, empty ModeledUser.
@@ -207,7 +207,7 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
 
         // Store previous password, if any
         if (model.getPasswordHash() != null)
-            this.previousPassword = new PasswordRecordModel(model);
+            this.passwordRecord = new PasswordRecordModel(model);
 
     }
 
@@ -245,19 +245,19 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
     }
 
     /**
-     * Returns the data associated with this user's previous password as a
-     * password record. If the user is new, this will be null. Unlike the other
-     * password-related functions of UserModel, this data returned by this
-     * function is historical and is unaffected by calls to setPassword(). It
-     * will always return the values stored in the database at the time this
-     * user was queried.
+     * Returns the this user's current password record. If the user is new, this
+     * will be null. Note that this may represent a different password than what
+     * is returned by getPassword(): unlike the other password-related functions
+     * of ModeledUser, the data returned by this function is historical and is
+     * unaffected by calls to setPassword(). It will always return the values
+     * stored in the database at the time this user was queried.
      *
      * @return
-     *     The data associated with this user's previous password, or null if
+     *     The historical data associated with this user's password, or null if
      *     the user is new.
      */
-    public PasswordRecordModel getPreviousPassword() {
-        return previousPassword;
+    public PasswordRecordModel getPasswordRecord() {
+        return passwordRecord;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/52dda6b5/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
index 74503b5..5939b04 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
@@ -243,7 +243,7 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
             passwordPolicyService.verifyPassword(object.getIdentifier(), object.getPassword());
 
             // Store previous password in history
-            passwordPolicyService.recordPreviousPassword(object);
+            passwordPolicyService.recordPassword(object);
 
         }
 


[5/7] incubator-guacamole-client git commit: GUACAMOLE-36: Record and maintain password history.

Posted by jm...@apache.org.
GUACAMOLE-36: Record and maintain password history.

Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/a943077d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/a943077d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/a943077d

Branch: refs/heads/master
Commit: a943077d4053804bced5d138164a78ef692a09df
Parents: ae695ef
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Aug 22 22:06:44 2016 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Thu Jan 5 13:37:13 2017 -0800

----------------------------------------------------------------------
 .../auth/jdbc/security/PasswordPolicy.java      | 14 ++++++++
 .../jdbc/security/PasswordPolicyService.java    | 35 ++++++++++++++++++++
 .../guacamole/auth/jdbc/user/UserService.java   |  3 ++
 .../auth/mysql/MySQLPasswordPolicy.java         | 18 ++++++++++
 .../auth/jdbc/user/PasswordRecordMapper.xml     | 14 +++++++-
 .../postgresql/PostgreSQLPasswordPolicy.java    | 18 ++++++++++
 .../auth/jdbc/user/PasswordRecordMapper.xml     | 11 +++++-
 7 files changed, 111 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/a943077d/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicy.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicy.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicy.java
index 8413b6a..7684487 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicy.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicy.java
@@ -73,6 +73,20 @@ public interface PasswordPolicy {
     int getMaximumAge() throws GuacamoleException;
 
     /**
+     * Returns the number of previous passwords remembered for each user. If
+     * greater than zero, users will be prohibited from reusing their past
+     * passwords.
+     *
+     * @return
+     *     The number of previous passwords remembered for each user.
+     *
+     * @throws GuacamoleException
+     *     If the password history size cannot be parsed from
+     *     guacamole.properties.
+     */
+    int getHistorySize() throws GuacamoleException;
+
+    /**
      * Returns whether both uppercase and lowercase characters must be present
      * in new passwords. If true, passwords which do not have at least one
      * uppercase letter and one lowercase letter cannot be used.

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/a943077d/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
index dfa980c..2b6c74c 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java
@@ -26,6 +26,7 @@ import java.util.regex.Pattern;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
 import org.apache.guacamole.auth.jdbc.user.ModeledUser;
+import org.apache.guacamole.auth.jdbc.user.PasswordRecordMapper;
 import org.apache.guacamole.auth.jdbc.user.PasswordRecordModel;
 
 /**
@@ -43,6 +44,12 @@ public class PasswordPolicyService {
     private JDBCEnvironment environment;
 
     /**
+     * Mapper for creating/retrieving previously-set passwords.
+     */
+    @Inject
+    private PasswordRecordMapper passwordRecordMapper;
+
+    /**
      * Regular expression which matches only if the string contains at least one
      * lowercase character.
      */
@@ -235,4 +242,32 @@ public class PasswordPolicyService {
 
     }
 
+    /**
+     * Records the password that was associated with the given user at the time
+     * the user was queried, such that future attempts to set that same password
+     * for that user will be denied. The number of passwords remembered for each
+     * user is limited by the password policy.
+     *
+     * @param user
+     *     The user whose previous password should be recorded.
+     *
+     * @throws GuacamoleException
+     *     If the password policy cannot be parsed.
+     */
+    public void recordPreviousPassword(ModeledUser user)
+            throws GuacamoleException {
+
+        // Retrieve password policy from environment
+        PasswordPolicy policy = environment.getPasswordPolicy();
+
+        // Nothing to do if history is not being recorded
+        int historySize = policy.getHistorySize();
+        if (historySize <= 0)
+            return;
+        
+        // Store previous password in history
+        passwordRecordMapper.insert(user.getPreviousPassword(), historySize);
+
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/a943077d/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
index 5bfd665..74503b5 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
@@ -242,6 +242,9 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
             // Always verify password complexity
             passwordPolicyService.verifyPassword(object.getIdentifier(), object.getPassword());
 
+            // Store previous password in history
+            passwordPolicyService.recordPreviousPassword(object);
+
         }
 
     }

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/a943077d/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLPasswordPolicy.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLPasswordPolicy.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLPasswordPolicy.java
index 0ee686c..513eb0e 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLPasswordPolicy.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLPasswordPolicy.java
@@ -72,6 +72,19 @@ public class MySQLPasswordPolicy implements PasswordPolicy {
     };
 
     /**
+     * The property which specifies the number of previous passwords remembered
+     * for each user. If set to zero, the default, then this restriction does
+     * not apply.
+     */
+    private static final IntegerGuacamoleProperty HISTORY_SIZE =
+            new IntegerGuacamoleProperty() {
+
+        @Override
+        public String getName() { return "mysql-user-password-history-size"; }
+
+    };
+
+    /**
      * The property which specifies whether all user passwords must have at
      * least one lowercase character and one uppercase character. By default,
      * no such restriction is imposed.
@@ -156,6 +169,11 @@ public class MySQLPasswordPolicy implements PasswordPolicy {
     }
 
     @Override
+    public int getHistorySize() throws GuacamoleException {
+        return environment.getProperty(HISTORY_SIZE, 0);
+    }
+
+    @Override
     public boolean isMultipleCaseRequired() throws GuacamoleException {
         return environment.getProperty(REQUIRE_MULTIPLE_CASE, false);
     }

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/a943077d/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
index 2b5ff23..6d79a8e 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
@@ -63,7 +63,19 @@
             #{record.passwordHash,jdbcType=BINARY},
             #{record.passwordSalt,jdbcType=BINARY},
             #{record.passwordDate,jdbcType=TIMESTAMP}
-        )
+        );
+
+        DELETE FROM guacamole_user_password_history
+        WHERE password_history_id <= (
+            SELECT password_history_id
+            FROM (
+                SELECT password_history_id
+                FROM guacamole_user_password_history
+                WHERE user_id = #{record.userID,jdbcType=INTEGER}
+                ORDER BY password_date DESC
+                LIMIT 1 OFFSET #{maxHistorySize}
+            ) old_password_record
+        );
 
     </insert>
 

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/a943077d/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLPasswordPolicy.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLPasswordPolicy.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLPasswordPolicy.java
index 002c96b..1e46f9a 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLPasswordPolicy.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLPasswordPolicy.java
@@ -72,6 +72,19 @@ public class PostgreSQLPasswordPolicy implements PasswordPolicy {
     };
 
     /**
+     * The property which specifies the number of previous passwords remembered
+     * for each user. If set to zero, the default, then this restriction does
+     * not apply.
+     */
+    private static final IntegerGuacamoleProperty HISTORY_SIZE =
+            new IntegerGuacamoleProperty() {
+
+        @Override
+        public String getName() { return "postgresql-user-password-history-size"; }
+
+    };
+
+    /**
      * The property which specifies whether all user passwords must have at
      * least one lowercase character and one uppercase character. By default,
      * no such restriction is imposed.
@@ -156,6 +169,11 @@ public class PostgreSQLPasswordPolicy implements PasswordPolicy {
     }
 
     @Override
+    public int getHistorySize() throws GuacamoleException {
+        return environment.getProperty(HISTORY_SIZE, 0);
+    }
+
+    @Override
     public boolean isMultipleCaseRequired() throws GuacamoleException {
         return environment.getProperty(REQUIRE_MULTIPLE_CASE, false);
     }

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/a943077d/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
index a119f4c..41591fa 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
@@ -63,7 +63,16 @@
             #{record.passwordHash,jdbcType=BINARY},
             #{record.passwordSalt,jdbcType=BINARY},
             #{record.passwordDate,jdbcType=TIMESTAMP}
-        )
+        );
+
+        DELETE FROM guacamole_user_password_history
+        WHERE password_history_id IN (
+            SELECT password_history_id
+            FROM guacamole_user_password_history
+            WHERE user_id = #{record.userID,jdbcType=INTEGER}
+            ORDER BY password_date DESC
+            OFFSET #{maxHistorySize}
+        );
 
     </insert>
 


[6/7] incubator-guacamole-client git commit: GUACAMOLE-36: Clarify semantics of password policy history size.

Posted by jm...@apache.org.
GUACAMOLE-36: Clarify semantics of password policy history size.

Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/497480cc
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/497480cc
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/497480cc

Branch: refs/heads/master
Commit: 497480cc5341c06d5935086bcd0b038ed5ca794b
Parents: 52dda6b
Author: Michael Jumper <mj...@apache.org>
Authored: Thu Jan 5 13:44:16 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Thu Jan 5 13:44:16 2017 -0800

----------------------------------------------------------------------
 .../org/apache/guacamole/auth/jdbc/security/PasswordPolicy.java  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/497480cc/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicy.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicy.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicy.java
index 7684487..3ee122a 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicy.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicy.java
@@ -74,8 +74,8 @@ public interface PasswordPolicy {
 
     /**
      * Returns the number of previous passwords remembered for each user. If
-     * greater than zero, users will be prohibited from reusing their past
-     * passwords.
+     * greater than zero, users will be prohibited from reusing those passwords.
+     *
      *
      * @return
      *     The number of previous passwords remembered for each user.


[3/7] incubator-guacamole-client git commit: GUACAMOLE-36: Define and map historical password record table.

Posted by jm...@apache.org.
GUACAMOLE-36: Define and map historical password record table.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/ae695ef1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/ae695ef1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/ae695ef1

Branch: refs/heads/master
Commit: ae695ef17b2272fefa4941c1a69b1816ffd9b8e9
Parents: 3c718f2
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Aug 22 17:24:38 2016 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Thu Jan 5 13:37:13 2017 -0800

----------------------------------------------------------------------
 .../jdbc/JDBCAuthenticationProviderModule.java  |  2 +
 .../auth/jdbc/user/PasswordRecordMapper.java    | 68 +++++++++++++++++++
 .../schema/001-create-schema.sql                | 22 ++++++
 .../schema/upgrade/upgrade-pre-0.9.11.sql       | 23 +++++++
 .../auth/jdbc/user/PasswordRecordMapper.xml     | 70 ++++++++++++++++++++
 .../schema/001-create-schema.sql                | 24 +++++++
 .../schema/upgrade/upgrade-pre-0.9.11.sql       | 24 +++++++
 .../auth/jdbc/user/PasswordRecordMapper.xml     | 70 ++++++++++++++++++++
 8 files changed, 303 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/ae695ef1/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java
index e52ca5a..475282f 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java
@@ -75,6 +75,7 @@ import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileMapper;
 import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapper;
 import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileService;
 import org.apache.guacamole.auth.jdbc.tunnel.RestrictedGuacamoleTunnelService;
+import org.apache.guacamole.auth.jdbc.user.PasswordRecordMapper;
 import org.mybatis.guice.MyBatisModule;
 import org.mybatis.guice.datasource.builtin.PooledDataSourceProvider;
 
@@ -121,6 +122,7 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule {
         addMapperClass(ConnectionPermissionMapper.class);
         addMapperClass(ConnectionRecordMapper.class);
         addMapperClass(ConnectionParameterMapper.class);
+        addMapperClass(PasswordRecordMapper.class);
         addMapperClass(SystemPermissionMapper.class);
         addMapperClass(SharingProfileMapper.class);
         addMapperClass(SharingProfileParameterMapper.class);

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/ae695ef1/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.java
new file mode 100644
index 0000000..21d36a8
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.auth.jdbc.user;
+
+import java.util.List;
+import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * Mapper for historical password records (users' prior passwords, along with
+ * the dates they were set).
+ *
+ * @author Michael Jumper
+ */
+public interface PasswordRecordMapper extends ModeledDirectoryObjectMapper<UserModel> {
+
+    /**
+     * Returns a collection of all password records associated with the user
+     * having the given username.
+     *
+     * @param username
+     *     The username of the user whose password records are to be retrieved.
+     *
+     * @param maxHistorySize
+     *     The maximum number of records to maintain for each user.
+     *
+     * @return
+     *     A collection of all password records associated with the user having
+     *     the given username. This collection will be empty if no such user
+     *     exists.
+     */
+    List<PasswordRecordModel> select(@Param("username") String username,
+            @Param("maxHistorySize") int maxHistorySize);
+
+    /**
+     * Inserts the given password record. Old records exceeding the maximum
+     * history size will be automatically deleted.
+     *
+     * @param record
+     *     The password record to insert.
+     *
+     * @param maxHistorySize
+     *     The maximum number of records to maintain for each user.
+     *
+     * @return
+     *     The number of rows inserted.
+     */
+    int insert(@Param("record") PasswordRecordModel record,
+            @Param("maxHistorySize") int maxHistorySize);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/ae695ef1/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql
index cb56047..ebf5b7d 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql
@@ -336,3 +336,25 @@ CREATE TABLE `guacamole_connection_history` (
 
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
+--
+-- User password history
+--
+
+CREATE TABLE guacamole_user_password_history (
+
+  `password_history_id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id`             int(11) NOT NULL,
+
+  -- Salted password
+  `password_hash` binary(32) NOT NULL,
+  `password_salt` binary(32),
+  `password_date` datetime   NOT NULL,
+
+  PRIMARY KEY (`password_history_id`),
+  KEY `user_id` (`user_id`),
+
+  CONSTRAINT `guacamole_user_password_history_ibfk_1`
+    FOREIGN KEY (`user_id`)
+    REFERENCES `guacamole_user` (`user_id`) ON DELETE CASCADE
+
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/ae695ef1/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.9.11.sql
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.9.11.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.9.11.sql
index 3acc2a4..ecfde88 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.9.11.sql
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.9.11.sql
@@ -23,3 +23,26 @@
 
 ALTER TABLE guacamole_user
     ADD COLUMN password_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;
+
+--
+-- User password history
+--
+
+CREATE TABLE guacamole_user_password_history (
+
+  `password_history_id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id`             int(11) NOT NULL,
+
+  -- Salted password
+  `password_hash` binary(32) NOT NULL,
+  `password_salt` binary(32),
+  `password_date` datetime   NOT NULL,
+
+  PRIMARY KEY (`password_history_id`),
+  KEY `user_id` (`user_id`),
+
+  CONSTRAINT `guacamole_user_password_history_ibfk_1`
+    FOREIGN KEY (`user_id`)
+    REFERENCES `guacamole_user` (`user_id`) ON DELETE CASCADE
+
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/ae695ef1/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
new file mode 100644
index 0000000..2b5ff23
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+
+<mapper namespace="org.apache.guacamole.auth.jdbc.user.PasswordRecordMapper" >
+
+    <!-- Result mapper for system permissions -->
+    <resultMap id="PasswordRecordResultMap" type="org.apache.guacamole.auth.jdbc.user.PasswordRecordModel">
+        <result column="user_id"       property="userID"       jdbcType="INTEGER"/>
+        <result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
+        <result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
+        <result column="password_date" property="passwordDate" jdbcType="TIMESTAMP"/>
+    </resultMap>
+
+    <!-- Select all password records for a given user -->
+    <select id="select" resultMap="PasswordRecordResultMap">
+
+        SELECT
+            guacamole_user_password_history.user_id,
+            guacamole_user_password_history.password_hash,
+            guacamole_user_password_history.password_salt,
+            guacamole_user_password_history.password_date
+        FROM guacamole_user_password_history
+        JOIN guacamole_user ON guacamole_user_password_history.user_id = guacamole_user.user_id
+        WHERE
+            guacamole_user.username = #{username,jdbcType=VARCHAR}
+        ORDER BY
+            guacamole_user_password_history.password_date DESC
+        LIMIT #{maxHistorySize}
+
+    </select>
+
+    <!-- Insert the given password record -->
+    <insert id="insert" parameterType="org.apache.guacamole.auth.jdbc.user.PasswordRecordModel">
+
+        INSERT INTO guacamole_user_password_history (
+            user_id,
+            password_hash,
+            password_salt,
+            password_date
+        )
+        VALUES (
+            #{record.userID,jdbcType=INTEGER},
+            #{record.passwordHash,jdbcType=BINARY},
+            #{record.passwordSalt,jdbcType=BINARY},
+            #{record.passwordDate,jdbcType=TIMESTAMP}
+        )
+
+    </insert>
+
+</mapper>

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/ae695ef1/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql
index e308dce..f2ad2c4 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql
@@ -385,3 +385,27 @@ CREATE INDEX ON guacamole_connection_history(connection_id);
 CREATE INDEX ON guacamole_connection_history(sharing_profile_id);
 CREATE INDEX ON guacamole_connection_history(start_date);
 CREATE INDEX ON guacamole_connection_history(end_date);
+
+--
+-- User password history
+--
+
+CREATE TABLE guacamole_user_password_history (
+
+  password_history_id serial  NOT NULL,
+  user_id             integer NOT NULL,
+
+  -- Salted password
+  password_hash bytea        NOT NULL,
+  password_salt bytea,
+  password_date timestamptz  NOT NULL,
+
+  PRIMARY KEY (password_history_id),
+
+  CONSTRAINT guacamole_user_password_history_ibfk_1
+    FOREIGN KEY (user_id)
+    REFERENCES guacamole_user (user_id) ON DELETE CASCADE
+
+);
+
+CREATE INDEX ON guacamole_user_password_history(user_id);

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/ae695ef1/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.11.sql
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.11.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.11.sql
index d2f430c..c047a8f 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.11.sql
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.11.sql
@@ -23,3 +23,27 @@
 
 ALTER TABLE guacamole_user
     ADD COLUMN password_date timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP;
+
+--
+-- User password history
+--
+
+CREATE TABLE guacamole_user_password_history (
+
+  password_history_id serial  NOT NULL,
+  user_id             integer NOT NULL,
+
+  -- Salted password
+  password_hash bytea        NOT NULL,
+  password_salt bytea,
+  password_date timestamptz  NOT NULL,
+
+  PRIMARY KEY (password_history_id),
+
+  CONSTRAINT guacamole_user_password_history_ibfk_1
+    FOREIGN KEY (user_id)
+    REFERENCES guacamole_user (user_id) ON DELETE CASCADE
+
+);
+
+CREATE INDEX ON guacamole_user_password_history(user_id);

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/ae695ef1/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
new file mode 100644
index 0000000..a119f4c
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+
+<mapper namespace="org.apache.guacamole.auth.jdbc.user.PasswordRecordMapper" >
+
+    <!-- Result mapper for historical passwords -->
+    <resultMap id="PasswordRecordResultMap" type="org.apache.guacamole.auth.jdbc.user.PasswordRecordModel">
+        <result column="user_id"       property="userID"       jdbcType="INTEGER"/>
+        <result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
+        <result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
+        <result column="password_date" property="passwordDate" jdbcType="TIMESTAMP"/>
+    </resultMap>
+
+    <!-- Select all password records for a given user -->
+    <select id="select" resultMap="PasswordRecordResultMap">
+
+        SELECT
+            guacamole_user_password_history.user_id,
+            guacamole_user_password_history.password_hash,
+            guacamole_user_password_history.password_salt,
+            guacamole_user_password_history.password_date
+        FROM guacamole_user_password_history
+        JOIN guacamole_user ON guacamole_user_password_history.user_id = guacamole_user.user_id
+        WHERE
+            guacamole_user.username = #{username,jdbcType=VARCHAR}
+        ORDER BY
+            guacamole_user_password_history.password_date DESC
+        LIMIT #{maxHistorySize}
+
+    </select>
+
+    <!-- Insert the given password record -->
+    <insert id="insert" parameterType="org.apache.guacamole.auth.jdbc.user.PasswordRecordModel">
+
+        INSERT INTO guacamole_user_password_history (
+            user_id,
+            password_hash,
+            password_salt,
+            password_date
+        )
+        VALUES (
+            #{record.userID,jdbcType=INTEGER},
+            #{record.passwordHash,jdbcType=BINARY},
+            #{record.passwordSalt,jdbcType=BINARY},
+            #{record.passwordDate,jdbcType=TIMESTAMP}
+        )
+
+    </insert>
+
+</mapper>


[7/7] incubator-guacamole-client git commit: GUACAMOLE-36: Merge password history tracking change.

Posted by jm...@apache.org.
GUACAMOLE-36: Merge password history tracking change.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/d45580f2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/d45580f2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/d45580f2

Branch: refs/heads/master
Commit: d45580f226537e49c96e3b30cd793bcc31e6cca5
Parents: 102d52f 497480c
Author: James Muehlner <ja...@guac-dev.org>
Authored: Thu Jan 5 13:49:29 2017 -0800
Committer: James Muehlner <ja...@guac-dev.org>
Committed: Thu Jan 5 13:49:29 2017 -0800

----------------------------------------------------------------------
 .../jdbc/JDBCAuthenticationProviderModule.java  |   2 +
 .../auth/jdbc/security/PasswordPolicy.java      |  14 ++
 .../jdbc/security/PasswordPolicyService.java    | 101 +++++++++++-
 .../jdbc/security/PasswordReusedException.java  |  52 +++++++
 .../guacamole/auth/jdbc/user/ModeledUser.java   |  33 ++--
 .../auth/jdbc/user/PasswordRecordMapper.java    |  68 ++++++++
 .../auth/jdbc/user/PasswordRecordModel.java     | 156 +++++++++++++++++++
 .../guacamole/auth/jdbc/user/UserService.java   |   3 +
 .../src/main/resources/translations/en.json     |   1 +
 .../schema/001-create-schema.sql                |  22 +++
 .../schema/upgrade/upgrade-pre-0.9.11.sql       |  23 +++
 .../auth/mysql/MySQLPasswordPolicy.java         |  18 +++
 .../auth/jdbc/user/PasswordRecordMapper.xml     |  82 ++++++++++
 .../schema/001-create-schema.sql                |  24 +++
 .../schema/upgrade/upgrade-pre-0.9.11.sql       |  24 +++
 .../postgresql/PostgreSQLPasswordPolicy.java    |  18 +++
 .../auth/jdbc/user/PasswordRecordMapper.xml     |  79 ++++++++++
 17 files changed, 705 insertions(+), 15 deletions(-)
----------------------------------------------------------------------