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 18:27:17 UTC
[3/5] incubator-guacamole-client git commit: GUACAMOLE-36: Implement
password aging checks.
GUACAMOLE-36: Implement password aging checks.
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/5f6fb8a1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/5f6fb8a1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/5f6fb8a1
Branch: refs/heads/master
Commit: 5f6fb8a1994206a85bd71b57b877fa24c630b7c3
Parents: a207411
Author: Michael Jumper <mj...@apache.org>
Authored: Fri Aug 19 17:35:12 2016 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Thu Jan 5 06:30:40 2017 -0800
----------------------------------------------------------------------
.../jdbc/JDBCAuthenticationProviderService.java | 9 ++-
.../auth/jdbc/security/PasswordPolicy.java | 30 +++++++
.../jdbc/security/PasswordPolicyService.java | 83 ++++++++++++++++++++
.../security/PasswordTooYoungException.java | 53 +++++++++++++
.../guacamole/auth/jdbc/user/UserService.java | 17 +++-
.../src/main/resources/translations/en.json | 3 +-
.../auth/mysql/MySQLPasswordPolicy.java | 36 +++++++++
.../postgresql/PostgreSQLPasswordPolicy.java | 36 +++++++++
8 files changed, 264 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/5f6fb8a1/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java
index a0d422a..9839055 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java
@@ -22,6 +22,7 @@ package org.apache.guacamole.auth.jdbc;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.auth.jdbc.security.PasswordPolicyService;
import org.apache.guacamole.auth.jdbc.sharing.user.SharedAuthenticatedUser;
import org.apache.guacamole.auth.jdbc.user.ModeledUser;
import org.apache.guacamole.auth.jdbc.user.ModeledUserContext;
@@ -56,6 +57,12 @@ public class JDBCAuthenticationProviderService implements AuthenticationProvider
private UserService userService;
/**
+ * Service for enforcing password complexity policies.
+ */
+ @Inject
+ private PasswordPolicyService passwordPolicyService;
+
+ /**
* Provider for retrieving UserContext instances.
*/
@Inject
@@ -101,7 +108,7 @@ public class JDBCAuthenticationProviderService implements AuthenticationProvider
// Update password if password is expired
UserModel userModel = user.getModel();
- if (userModel.isExpired())
+ if (userModel.isExpired() || passwordPolicyService.isPasswordExpired(userModel))
userService.resetExpiredPassword(user, authenticatedUser.getCredentials());
// Link to user context
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/5f6fb8a1/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 8526bf6..eb02ba3 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
@@ -43,6 +43,36 @@ public interface PasswordPolicy {
int getMinimumLength() throws GuacamoleException;
/**
+ * Returns the minimum number of days which must elapse before the user's
+ * password may be reset. If this restriction does not apply, this will be
+ * zero.
+ *
+ * @return
+ * The minimum number of days which must elapse before the user's
+ * password must be reset, or zero if this restriction does not apply.
+ *
+ * @throws GuacamoleException
+ * If the minimum password age cannot be parsed from
+ * guacamole.properties.
+ */
+ int getMinimumAge() throws GuacamoleException;
+
+ /**
+ * Returns the maximum number of days which may elapse before the user's
+ * password must be reset. If this restriction does not apply, this will be
+ * zero.
+ *
+ * @return
+ * The maximum number of days which may elapse before the user's
+ * password must be reset, or zero if this restriction does not apply.
+ *
+ * @throws GuacamoleException
+ * If the maximum password age cannot be parsed from
+ * guacamole.properties.
+ */
+ int getMaximumAge() 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/5f6fb8a1/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 8d3c0b6..23fc367 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,10 +20,12 @@
package org.apache.guacamole.auth.jdbc.security;
import com.google.inject.Inject;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
+import org.apache.guacamole.auth.jdbc.user.UserModel;
/**
* Service which verifies compliance with the password policy configured via
@@ -146,4 +148,85 @@ public class PasswordPolicyService {
}
+ /**
+ * Returns the age of the given user's password, in days. The age of a
+ * user's password is the amount of time elapsed since the password was last
+ * changed or reset.
+ *
+ * @param user
+ * The user to calculate the password age of.
+ *
+ * @return
+ * The age of the given user's password, in days.
+ */
+ private long getPasswordAge(UserModel user) {
+
+ // Pull both current time and the time the password was last reset
+ long currentTime = System.currentTimeMillis();
+ long lastResetTime = user.getPasswordDate().getTime();
+
+ // Calculate the number of days elapsed since the password was last reset
+ return TimeUnit.DAYS.convert(currentTime - lastResetTime, TimeUnit.MILLISECONDS);
+
+ }
+
+ /**
+ * Verifies that the given user can change their password without violating
+ * password aging policy. If changing the user's password would violate the
+ * aging policy, a GuacamoleException will be thrown.
+ *
+ * @param user
+ * The user whose password is changing.
+ *
+ * @throws GuacamoleException
+ * If the user's password cannot be changed due to the password aging
+ * policy, or of the password policy cannot be parsed from
+ * guacamole.properties.
+ */
+ public void verifyPasswordAge(UserModel user) throws GuacamoleException {
+
+ // Retrieve password policy from environment
+ PasswordPolicy policy = environment.getPasswordPolicy();
+
+ long minimumAge = policy.getMinimumAge();
+ long passwordAge = getPasswordAge(user);
+
+ // Require that sufficient time has elapsed before allowing the password
+ // to be changed
+ if (passwordAge < minimumAge)
+ throw new PasswordTooYoungException("Password was already recently changed.",
+ minimumAge - passwordAge);
+
+ }
+
+ /**
+ * Returns whether the given user's password is expired due to the password
+ * aging policy.
+ *
+ * @param user
+ * The user to check.
+ *
+ * @return
+ * true if the user needs to change their password to comply with the
+ * password aging policy, false otherwise.
+ *
+ * @throws GuacamoleException
+ * If the password policy cannot be parsed.
+ */
+ public boolean isPasswordExpired(UserModel user)
+ throws GuacamoleException {
+
+ // Retrieve password policy from environment
+ PasswordPolicy policy = environment.getPasswordPolicy();
+
+ // There is no maximum password age if 0
+ int maxPasswordAge = policy.getMaximumAge();
+ if (maxPasswordAge == 0)
+ return false;
+
+ // Determine whether password is expired based on maximum age
+ return getPasswordAge(user) >= maxPasswordAge;
+
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/5f6fb8a1/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordTooYoungException.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordTooYoungException.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordTooYoungException.java
new file mode 100644
index 0000000..a5235dc
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordTooYoungException.java
@@ -0,0 +1,53 @@
+/*
+ * 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 set a user's password before sufficient
+ * time has elapsed since the password was last reset, in violation of the
+ * defined password policy.
+ *
+ * @author Michael Jumper
+ */
+public class PasswordTooYoungException extends PasswordPolicyException {
+
+ /**
+ * Creates a new PasswordTooYoungException 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 wait
+ * The number of days the user should wait before attempting to reset
+ * the password again.
+ */
+ public PasswordTooYoungException(String message, long wait) {
+ super(message, new TranslatableMessage(
+ "PASSWORD_POLICY.ERROR_TOO_YOUNG",
+ Collections.singletonMap("WAIT", wait)
+ ));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/5f6fb8a1/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 e131841..25dfa32 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
@@ -21,6 +21,7 @@ package org.apache.guacamole.auth.jdbc.user;
import com.google.inject.Inject;
import com.google.inject.Provider;
+import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -210,6 +211,9 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
if (object.getPassword() != null)
passwordPolicyService.verifyPassword(object.getIdentifier(), object.getPassword());
+ // Update password reset date
+ model.setPasswordDate(new Timestamp(System.currentTimeMillis()));
+
}
@Override
@@ -233,9 +237,20 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
}
// Verify new password does not violate defined policies (if specified)
- if (object.getPassword() != null)
+ if (object.getPassword() != null) {
+
+ // Enforce password age only for non-adminstrators
+ if (!user.getUser().isAdministrator())
+ passwordPolicyService.verifyPasswordAge(model);
+
+ // Always verify password complexity
passwordPolicyService.verifyPassword(object.getIdentifier(), object.getPassword());
+ // Update password reset date
+ model.setPasswordDate(new Timestamp(System.currentTimeMillis()));
+
+ }
+
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/5f6fb8a1/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 3ee983f..95bb59b 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,7 +60,8 @@
"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_TOO_SHORT" : "Passwords must be at least {LENGTH} {LENGTH, plural, one{character} other{characters}} long."
+ "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."
},
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/5f6fb8a1/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 3d1ca71..0ee686c 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
@@ -46,6 +46,32 @@ public class MySQLPasswordPolicy implements PasswordPolicy {
};
/**
+ * The property which specifies the minimum number of days which must
+ * elapse before a user may reset their password. If set to zero, the
+ * default, then this restriction does not apply.
+ */
+ private static final IntegerGuacamoleProperty MIN_AGE =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "mysql-user-password-min-age"; }
+
+ };
+
+ /**
+ * The property which specifies the maximum number of days which may
+ * elapse before a user is required to reset their password. If set to zero,
+ * the default, then this restriction does not apply.
+ */
+ private static final IntegerGuacamoleProperty MAX_AGE =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "mysql-user-password-max-age"; }
+
+ };
+
+ /**
* 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.
@@ -120,6 +146,16 @@ public class MySQLPasswordPolicy implements PasswordPolicy {
}
@Override
+ public int getMinimumAge() throws GuacamoleException {
+ return environment.getProperty(MIN_AGE, 0);
+ }
+
+ @Override
+ public int getMaximumAge() throws GuacamoleException {
+ return environment.getProperty(MAX_AGE, 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/5f6fb8a1/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 adbee2a..002c96b 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
@@ -46,6 +46,32 @@ public class PostgreSQLPasswordPolicy implements PasswordPolicy {
};
/**
+ * The property which specifies the minimum number of days which must
+ * elapse before a user may reset their password. If set to zero, the
+ * default, then this restriction does not apply.
+ */
+ private static final IntegerGuacamoleProperty MIN_AGE =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "postgresql-user-password-min-age"; }
+
+ };
+
+ /**
+ * The property which specifies the maximum number of days which may
+ * elapse before a user is required to reset their password. If set to zero,
+ * the default, then this restriction does not apply.
+ */
+ private static final IntegerGuacamoleProperty MAX_AGE =
+ new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "postgresql-user-password-max-age"; }
+
+ };
+
+ /**
* 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.
@@ -120,6 +146,16 @@ public class PostgreSQLPasswordPolicy implements PasswordPolicy {
}
@Override
+ public int getMinimumAge() throws GuacamoleException {
+ return environment.getProperty(MIN_AGE, 0);
+ }
+
+ @Override
+ public int getMaximumAge() throws GuacamoleException {
+ return environment.getProperty(MAX_AGE, 0);
+ }
+
+ @Override
public boolean isMultipleCaseRequired() throws GuacamoleException {
return environment.getProperty(REQUIRE_MULTIPLE_CASE, false);
}