GUACAMOLE-36: Define and enforce password policies.

This commit is contained in:
Michael Jumper
2016-08-17 15:19:47 -07:00
parent 14e8c6fc50
commit 3744755a1e
16 changed files with 899 additions and 1 deletions

View File

@@ -63,6 +63,7 @@ import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper;
import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionMapper; import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionMapper;
import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionService; import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionService;
import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionSet; import org.apache.guacamole.auth.jdbc.permission.SharingProfilePermissionSet;
import org.apache.guacamole.auth.jdbc.security.PasswordPolicyService;
import org.apache.guacamole.auth.jdbc.sharing.ConnectionSharingService; import org.apache.guacamole.auth.jdbc.sharing.ConnectionSharingService;
import org.apache.guacamole.auth.jdbc.sharing.HashSharedConnectionMap; import org.apache.guacamole.auth.jdbc.sharing.HashSharedConnectionMap;
import org.apache.guacamole.auth.jdbc.sharing.SecureRandomShareKeyGenerator; import org.apache.guacamole.auth.jdbc.sharing.SecureRandomShareKeyGenerator;
@@ -159,6 +160,7 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule {
bind(ConnectionService.class); bind(ConnectionService.class);
bind(GuacamoleTunnelService.class).to(RestrictedGuacamoleTunnelService.class); bind(GuacamoleTunnelService.class).to(RestrictedGuacamoleTunnelService.class);
bind(PasswordEncryptionService.class).to(SHA256PasswordEncryptionService.class); bind(PasswordEncryptionService.class).to(SHA256PasswordEncryptionService.class);
bind(PasswordPolicyService.class);
bind(SaltService.class).to(SecureRandomSaltService.class); bind(SaltService.class).to(SecureRandomSaltService.class);
bind(SharedConnectionMap.class).to(HashSharedConnectionMap.class).in(Scopes.SINGLETON); bind(SharedConnectionMap.class).to(HashSharedConnectionMap.class).in(Scopes.SINGLETON);
bind(ShareKeyGenerator.class).to(SecureRandomShareKeyGenerator.class).in(Scopes.SINGLETON); bind(ShareKeyGenerator.class).to(SecureRandomShareKeyGenerator.class).in(Scopes.SINGLETON);

View File

@@ -21,6 +21,7 @@ package org.apache.guacamole.auth.jdbc;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.environment.LocalEnvironment; import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
/** /**
* A JDBC-specific implementation of Environment that defines generic properties * A JDBC-specific implementation of Environment that defines generic properties
@@ -128,4 +129,14 @@ public abstract class JDBCEnvironment extends LocalEnvironment {
public abstract int getDefaultMaxGroupConnectionsPerUser() public abstract int getDefaultMaxGroupConnectionsPerUser()
throws GuacamoleException; throws GuacamoleException;
/**
* Returns the policy which applies to newly-set passwords. Passwords which
* apply to Guacamole user accounts will be required to conform to this
* policy.
*
* @return
* The password policy which applies to Guacamole user accounts.
*/
public abstract PasswordPolicy getPasswordPolicy();
} }

View File

@@ -0,0 +1,42 @@
/*
* 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;
/**
* Thrown when an attempt is made to set a user's password to a string which
* contains their own username, in violation of the defined password policy.
*
* @author Michael Jumper
*/
public class PasswordContainsUsernameException extends PasswordPolicyException {
/**
* Creates a new PasswordContainsUsernameException 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.
*/
public PasswordContainsUsernameException(String message) {
super(message, "PASSWORD_POLICY.ERROR_CONTAINS_USERNAME");
}
}

View File

@@ -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 set a user's password to a string which is
* too short, in violation of the defined password policy.
*
* @author Michael Jumper
*/
public class PasswordMinimumLengthException extends PasswordPolicyException {
/**
* Creates a new PasswordMinimumLengthException 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 length
* The minimum length that passwords must have to avoid violating
* policy, in characters.
*/
public PasswordMinimumLengthException(String message, int length) {
super(message, new TranslatableMessage(
"PASSWORD_POLICY.ERROR_TOO_SHORT",
Collections.singletonMap("LENGTH", length)
));
}
}

View File

@@ -0,0 +1,103 @@
/*
* 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 org.apache.guacamole.GuacamoleException;
/**
* A set of restrictions which define the level of complexity required for
* the passwords of Guacamole user accounts.
*
* @author Michael Jumper
*/
public interface PasswordPolicy {
/**
* Returns the minimum length of new passwords, in characters. Passwords
* which are shorter than this length cannot be used.
*
* @return
* The minimum number of characters required for new passwords.
*
* @throws GuacamoleException
* If the minimum password length cannot be parsed from
* guacamole.properties.
*/
int getMinimumLength() 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.
*
* @return
* true if both uppercase and lowercase characters must be present in
* new passwords, false otherwise.
*
* @throws GuacamoleException
* If the multiple case requirement cannot be parsed from
* guacamole.properties.
*/
boolean isMultipleCaseRequired() throws GuacamoleException;
/**
* Returns whether numeric characters (digits) must be present in new
* passwords. If true, passwords which do not have at least one numeric
* character cannot be used.
*
* @return
* true if numeric characters must be present in new passwords,
* false otherwise.
*
* @throws GuacamoleException
* If the numeric character requirement cannot be parsed from
* guacamole.properties.
*/
boolean isNumericRequired() throws GuacamoleException;
/**
* Returns whether non-alphanumeric characters (symbols) must be present in
* new passwords. If true, passwords which do not have at least one
* non-alphanumeric character cannot be used.
*
* @return
* true if non-alphanumeric characters must be present in new passwords,
* false otherwise.
*
* @throws GuacamoleException
* If the non-alphanumeric character requirement cannot be parsed from
* guacamole.properties.
*/
boolean isNonAlphanumericRequired() throws GuacamoleException;
/**
* Returns whether new passwords must not contain the user's own username.
*
* @return
* true if new passwords must not contain the user's own username,
* false otherwise.
*
* @throws GuacamoleException
* If the username password restriction cannot be parsed from
* guacamole.properties.
*/
boolean isUsernameProhibited() throws GuacamoleException;
}

View File

@@ -0,0 +1,86 @@
/*
* 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 org.apache.guacamole.GuacamoleClientException;
import org.apache.guacamole.language.Translatable;
import org.apache.guacamole.language.TranslatableMessage;
/**
* Thrown when an attempt to change a user's password fails due to a violation
* of password complexity policies.
*
* @author Michael Jumper
*/
public class PasswordPolicyException extends GuacamoleClientException
implements Translatable {
/**
* A translatable message which, after being passed through the translation
* system, describes the policy violation that occurred.
*/
private final TranslatableMessage translatableMessage;
/**
* Creates a new PasswordPolicyException with the given human-readable
* message (which will not be passed through the translation system) and
* translation string key(which WILL be passed through the translation
* system), both of which should describe the policy violation that
* occurred.
*
* @param message
* A human-readable message describing the policy violation that
* occurred.
*
* @param translationKey
* The key of a translation string known to the translation system
* which describes the policy violation that occurred.
*/
public PasswordPolicyException(String message, String translationKey) {
super(message);
this.translatableMessage = new TranslatableMessage(translationKey);
}
/**
* Creates a new PasswordPolicyException with the given human-readable
* message (which will not be passed through the translation system) and
* translatable message (which WILL be passed through the translation
* system), both of which should describe the policy violation that
* occurred.
*
* @param message
* A human-readable message describing the policy violation that
* occurred.
*
* @param translatableMessage
* A translatable message which, after being passed through the
* translation system, describes the policy violation that occurred.
*/
public PasswordPolicyException(String message, TranslatableMessage translatableMessage) {
super(message);
this.translatableMessage = translatableMessage;
}
@Override
public TranslatableMessage getTranslatableMessage() {
return translatableMessage;
}
}

View File

@@ -0,0 +1,149 @@
/*
* 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 com.google.inject.Inject;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
/**
* Service which verifies compliance with the password policy configured via
* guacamole.properties.
*
* @author Michael Jumper
*/
public class PasswordPolicyService {
/**
* The Guacamole server environment.
*/
@Inject
private JDBCEnvironment environment;
/**
* Regular expression which matches only if the string contains at least one
* lowercase character.
*/
private final Pattern CONTAINS_LOWERCASE = Pattern.compile("\\p{javaLowerCase}");
/**
* Regular expression which matches only if the string contains at least one
* uppercase character.
*/
private final Pattern CONTAINS_UPPERCASE = Pattern.compile("\\p{javaUpperCase}");
/**
* Regular expression which matches only if the string contains at least one
* numeric character.
*/
private final Pattern CONTAINS_DIGIT = Pattern.compile("\\p{Digit}");
/**
* Regular expression which matches only if the string contains at least one
* non-alphanumeric character.
*/
private final Pattern CONTAINS_NON_ALPHANUMERIC =
Pattern.compile("[^\\p{javaLowerCase}\\p{javaUpperCase}\\p{Digit}]");
/**
* Returns whether the given string matches all of the provided regular
* expressions.
*
* @param str
* The string to test against all provided regular expressions.
*
* @param patterns
* The regular expressions to match against the given string.
*
* @return
* true if the given string matches all provided regular expressions,
* false otherwise.
*/
private boolean matches(String str, Pattern... patterns) {
// Check given string against all provided patterns
for (Pattern pattern : patterns) {
// Fail overall test if any pattern fails to match
Matcher matcher = pattern.matcher(str);
if (!matcher.find())
return false;
}
// All provided patterns matched
return true;
}
/**
* 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.
*
* @param username
* The username of the user whose password is being changed.
*
* @param password
* The proposed new password.
*
* @throws GuacamoleException
* If the password policy cannot be parsed, or if the proposed password
* violates the password policy.
*/
public void verifyPassword(String username, String password)
throws GuacamoleException {
// Retrieve password policy from environment
PasswordPolicy policy = environment.getPasswordPolicy();
// Enforce minimum password length
if (password.length() < policy.getMinimumLength())
throw new PasswordMinimumLengthException(
"Password does not meet minimum length requirements.",
policy.getMinimumLength());
// Disallow passwords containing the username
if (policy.isUsernameProhibited() && password.toLowerCase().contains(username.toLowerCase()))
throw new PasswordContainsUsernameException(
"Password must not contain username.");
// Require both uppercase and lowercase characters
if (policy.isMultipleCaseRequired() && !matches(password, CONTAINS_LOWERCASE, CONTAINS_UPPERCASE))
throw new PasswordRequiresMultipleCaseException(
"Password must contain both uppercase and lowercase.");
// Require digits
if (policy.isNumericRequired() && !matches(password, CONTAINS_DIGIT))
throw new PasswordRequiresDigitException(
"Passwords must contain at least one digit.");
// Require non-alphanumeric symbols
if (policy.isNonAlphanumericRequired() && !matches(password, CONTAINS_NON_ALPHANUMERIC))
throw new PasswordRequiresSymbolException(
"Passwords must contain at least one non-alphanumeric character.");
// Password passes all defined restrictions
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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;
/**
* Thrown when an attempt is made to set a user's password to a string which
* contains no numeric characters (digits), in violation of the defined password
* policy.
*
* @author Michael Jumper
*/
public class PasswordRequiresDigitException extends PasswordPolicyException {
/**
* Creates a new PasswordRequiresDigitException 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.
*/
public PasswordRequiresDigitException(String message) {
super(message, "PASSWORD_POLICY.ERROR_REQUIRES_DIGIT");
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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;
/**
* Thrown when an attempt is made to set a user's password to a string which
* does not contain both uppercase and lowercase characters, in violation of the
* defined password policy.
*
* @author Michael Jumper
*/
public class PasswordRequiresMultipleCaseException extends PasswordPolicyException {
/**
* Creates a new PasswordRequiresMultipleCaseException 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.
*/
public PasswordRequiresMultipleCaseException(String message) {
super(message, "PASSWORD_POLICY.ERROR_REQUIRES_MULTIPLE_CASE");
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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;
/**
* Thrown when an attempt is made to set a user's password to a string which
* contains no non-alphanumeric characters (symbols), in violation of the
* defined password policy.
*
* @author Michael Jumper
*/
public class PasswordRequiresSymbolException extends PasswordPolicyException {
/**
* Creates a new PasswordRequiresSymbolException 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.
*/
public PasswordRequiresSymbolException(String message) {
super(message, "PASSWORD_POLICY.ERROR_REQUIRES_NON_ALNUM");
}
}

View File

@@ -35,6 +35,7 @@ import org.apache.guacamole.auth.jdbc.permission.ObjectPermissionMapper;
import org.apache.guacamole.auth.jdbc.permission.ObjectPermissionModel; import org.apache.guacamole.auth.jdbc.permission.ObjectPermissionModel;
import org.apache.guacamole.auth.jdbc.permission.UserPermissionMapper; import org.apache.guacamole.auth.jdbc.permission.UserPermissionMapper;
import org.apache.guacamole.auth.jdbc.security.PasswordEncryptionService; import org.apache.guacamole.auth.jdbc.security.PasswordEncryptionService;
import org.apache.guacamole.auth.jdbc.security.PasswordPolicyService;
import org.apache.guacamole.form.Field; import org.apache.guacamole.form.Field;
import org.apache.guacamole.form.PasswordField; import org.apache.guacamole.form.PasswordField;
import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticatedUser;
@@ -130,6 +131,12 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
@Inject @Inject
private PasswordEncryptionService encryptionService; private PasswordEncryptionService encryptionService;
/**
* Service for enforcing password complexity policies.
*/
@Inject
private PasswordPolicyService passwordPolicyService;
@Override @Override
protected ModeledDirectoryObjectMapper<UserModel> getObjectMapper() { protected ModeledDirectoryObjectMapper<UserModel> getObjectMapper() {
return userMapper; return userMapper;
@@ -199,6 +206,10 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
if (!existing.isEmpty()) if (!existing.isEmpty())
throw new GuacamoleClientException("User \"" + model.getIdentifier() + "\" already exists."); throw new GuacamoleClientException("User \"" + model.getIdentifier() + "\" already exists.");
// Verify new password does not violate defined policies (if specified)
if (object.getPassword() != null)
passwordPolicyService.verifyPassword(object.getIdentifier(), object.getPassword());
} }
@Override @Override
@@ -220,7 +231,11 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
throw new GuacamoleClientException("User \"" + model.getIdentifier() + "\" already exists."); throw new GuacamoleClientException("User \"" + model.getIdentifier() + "\" already exists.");
} }
// Verify new password does not violate defined policies (if specified)
if (object.getPassword() != null)
passwordPolicyService.verifyPassword(object.getIdentifier(), object.getPassword());
} }
@Override @Override
@@ -412,6 +427,9 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
if (!newPassword.equals(confirmNewPassword)) if (!newPassword.equals(confirmNewPassword))
throw new GuacamoleClientException("LOGIN.ERROR_PASSWORD_MISMATCH"); throw new GuacamoleClientException("LOGIN.ERROR_PASSWORD_MISMATCH");
// Verify new password does not violate defined policies
passwordPolicyService.verifyPassword(username, newPassword);
// Change password and reset expiration flag // Change password and reset expiration flag
userModel.setExpired(false); userModel.setExpired(false);
user.setPassword(newPassword); user.setPassword(newPassword);

View File

@@ -54,6 +54,16 @@
"INFO_SHARED_BY" : "Shared by {USERNAME}" "INFO_SHARED_BY" : "Shared by {USERNAME}"
}, },
"PASSWORD_POLICY" : {
"ERROR_CONTAINS_USERNAME" : "Passwords may not contain the username.",
"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."
},
"USER_ATTRIBUTES" : { "USER_ATTRIBUTES" : {
"FIELD_HEADER_DISABLED" : "Login disabled:", "FIELD_HEADER_DISABLED" : "Login disabled:",

View File

@@ -23,6 +23,7 @@ import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment; import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
/** /**
* A MySQL-specific implementation of JDBCEnvironment provides database * A MySQL-specific implementation of JDBCEnvironment provides database
@@ -220,6 +221,11 @@ public class MySQLEnvironment extends JDBCEnvironment {
); );
} }
@Override
public PasswordPolicy getPasswordPolicy() {
return new MySQLPasswordPolicy(this);
}
/** /**
* Returns the hostname of the MySQL server hosting the Guacamole * Returns the hostname of the MySQL server hosting the Guacamole
* authentication tables. If unspecified, this will be "localhost". * authentication tables. If unspecified, this will be "localhost".

View File

@@ -0,0 +1,142 @@
/*
* 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.mysql;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
import org.apache.guacamole.properties.BooleanGuacamoleProperty;
import org.apache.guacamole.properties.IntegerGuacamoleProperty;
/**
* PasswordPolicy implementation which reads the details of the policy from
* MySQL-specific properties in guacamole.properties.
*
* @author Michael Jumper
*/
public class MySQLPasswordPolicy implements PasswordPolicy {
/**
* The property which specifies the minimum length required of all user
* passwords. By default, this will be zero.
*/
private static final IntegerGuacamoleProperty MIN_LENGTH =
new IntegerGuacamoleProperty() {
@Override
public String getName() { return "mysql-user-password-min-length"; }
};
/**
* 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.
*/
private static final BooleanGuacamoleProperty REQUIRE_MULTIPLE_CASE =
new BooleanGuacamoleProperty() {
@Override
public String getName() { return "mysql-user-password-require-multiple-case"; }
};
/**
* The property which specifies whether all user passwords must have at
* least one numeric character (digit). By default, no such restriction is
* imposed.
*/
private static final BooleanGuacamoleProperty REQUIRE_DIGIT =
new BooleanGuacamoleProperty() {
@Override
public String getName() { return "mysql-user-password-require-digit"; }
};
/**
* The property which specifies whether all user passwords must have at
* least one non-alphanumeric character (symbol). By default, no such
* restriction is imposed.
*/
private static final BooleanGuacamoleProperty REQUIRE_SYMBOL =
new BooleanGuacamoleProperty() {
@Override
public String getName() { return "mysql-user-password-require-symbol"; }
};
/**
* The property which specifies whether users are prohibited from including
* their own username in their password. By default, no such restriction is
* imposed.
*/
private static final BooleanGuacamoleProperty PROHIBIT_USERNAME =
new BooleanGuacamoleProperty() {
@Override
public String getName() { return "mysql-user-password-prohibit-username"; }
};
/**
* The Guacamole server environment.
*/
private final JDBCEnvironment environment;
/**
* Creates a new MySQLPasswordPolicy which reads the details of the policy
* from the properties exposed by the given environment.
*
* @param environment
* The environment from which password policy properties should be
* read.
*/
public MySQLPasswordPolicy(JDBCEnvironment environment) {
this.environment = environment;
}
@Override
public int getMinimumLength() throws GuacamoleException {
return environment.getProperty(MIN_LENGTH, 0);
}
@Override
public boolean isMultipleCaseRequired() throws GuacamoleException {
return environment.getProperty(REQUIRE_MULTIPLE_CASE, false);
}
@Override
public boolean isNumericRequired() throws GuacamoleException {
return environment.getProperty(REQUIRE_DIGIT, false);
}
@Override
public boolean isNonAlphanumericRequired() throws GuacamoleException {
return environment.getProperty(REQUIRE_SYMBOL, false);
}
@Override
public boolean isUsernameProhibited() throws GuacamoleException {
return environment.getProperty(PROHIBIT_USERNAME, false);
}
}

View File

@@ -23,6 +23,7 @@ import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment; import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
/** /**
* A PostgreSQL-specific implementation of JDBCEnvironment provides database * A PostgreSQL-specific implementation of JDBCEnvironment provides database
@@ -219,6 +220,11 @@ public class PostgreSQLEnvironment extends JDBCEnvironment {
); );
} }
@Override
public PasswordPolicy getPasswordPolicy() {
return new PostgreSQLPasswordPolicy(this);
}
/** /**
* Returns the hostname of the PostgreSQL server hosting the Guacamole * Returns the hostname of the PostgreSQL server hosting the Guacamole
* authentication tables. If unspecified, this will be "localhost". * authentication tables. If unspecified, this will be "localhost".

View File

@@ -0,0 +1,142 @@
/*
* 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.postgresql;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
import org.apache.guacamole.properties.BooleanGuacamoleProperty;
import org.apache.guacamole.properties.IntegerGuacamoleProperty;
/**
* PasswordPolicy implementation which reads the details of the policy from
* PostgreSQL-specific properties in guacamole.properties.
*
* @author Michael Jumper
*/
public class PostgreSQLPasswordPolicy implements PasswordPolicy {
/**
* The property which specifies the minimum length required of all user
* passwords. By default, this will be zero.
*/
private static final IntegerGuacamoleProperty MIN_LENGTH =
new IntegerGuacamoleProperty() {
@Override
public String getName() { return "postgresql-user-password-min-length"; }
};
/**
* 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.
*/
private static final BooleanGuacamoleProperty REQUIRE_MULTIPLE_CASE =
new BooleanGuacamoleProperty() {
@Override
public String getName() { return "postgresql-user-password-require-multiple-case"; }
};
/**
* The property which specifies whether all user passwords must have at
* least one numeric character (digit). By default, no such restriction is
* imposed.
*/
private static final BooleanGuacamoleProperty REQUIRE_DIGIT =
new BooleanGuacamoleProperty() {
@Override
public String getName() { return "postgresql-user-password-require-digit"; }
};
/**
* The property which specifies whether all user passwords must have at
* least one non-alphanumeric character (symbol). By default, no such
* restriction is imposed.
*/
private static final BooleanGuacamoleProperty REQUIRE_SYMBOL =
new BooleanGuacamoleProperty() {
@Override
public String getName() { return "postgresql-user-password-require-symbol"; }
};
/**
* The property which specifies whether users are prohibited from including
* their own username in their password. By default, no such restriction is
* imposed.
*/
private static final BooleanGuacamoleProperty PROHIBIT_USERNAME =
new BooleanGuacamoleProperty() {
@Override
public String getName() { return "postgresql-user-password-prohibit-username"; }
};
/**
* The Guacamole server environment.
*/
private final JDBCEnvironment environment;
/**
* Creates a new PostgreSQLPasswordPolicy which reads the details of the
* policy from the properties exposed by the given environment.
*
* @param environment
* The environment from which password policy properties should be
* read.
*/
public PostgreSQLPasswordPolicy(JDBCEnvironment environment) {
this.environment = environment;
}
@Override
public int getMinimumLength() throws GuacamoleException {
return environment.getProperty(MIN_LENGTH, 0);
}
@Override
public boolean isMultipleCaseRequired() throws GuacamoleException {
return environment.getProperty(REQUIRE_MULTIPLE_CASE, false);
}
@Override
public boolean isNumericRequired() throws GuacamoleException {
return environment.getProperty(REQUIRE_DIGIT, false);
}
@Override
public boolean isNonAlphanumericRequired() throws GuacamoleException {
return environment.getProperty(REQUIRE_SYMBOL, false);
}
@Override
public boolean isUsernameProhibited() throws GuacamoleException {
return environment.getProperty(PROHIBIT_USERNAME, false);
}
}