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 8413b6a8d..7684487c5 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 @@ -72,6 +72,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 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 dfa980c0b..2b6c74cce 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; /** @@ -42,6 +43,12 @@ public class PasswordPolicyService { @Inject 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); + + } + } 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 5bfd665ad..74503b5b8 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 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 002c96b85..1e46f9aaf 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 @@ -71,6 +71,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, @@ -155,6 +168,11 @@ public class PostgreSQLPasswordPolicy implements PasswordPolicy { return environment.getProperty(MAX_AGE, 0); } + @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); 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 a119f4c3c..41591fada 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} + );