Index: kernel-impl/src/main/java/org/sakaiproject/user/impl/PasswordPolicyProviderDefaultImpl.java =================================================================== --- kernel-impl/src/main/java/org/sakaiproject/user/impl/PasswordPolicyProviderDefaultImpl.java (revision 130087) +++ kernel-impl/src/main/java/org/sakaiproject/user/impl/PasswordPolicyProviderDefaultImpl.java (working copy) @@ -32,10 +32,13 @@ /** * This is the default implementation of the Password policy provider. * - * @author bjones86 - OWL-831/RES-54 + * @author bjones86 + * * https://jira.sakaiproject.org/browse/KNL-1123 + * https://jira.sakaiproject.org/browse/RES-54 */ public class PasswordPolicyProviderDefaultImpl implements PasswordPolicyProvider { + /** Our log (commons). */ private static Log logger = LogFactory.getLog(PasswordPolicyProviderDefaultImpl.class); @@ -48,8 +51,11 @@ /** value for minimum password entropy */ private int minEntropy = DEFAULT_MIN_ENTROPY; - /** value for maximum password sequence length */ - private int maxSequenceLength = DEFAULT_MAX_SEQ_LENGTH; + /** value for maximum allowed password sequence length */ + private int maxAllowedSequenceLength = DEFAULT_MAX_ALLOWED_SEQ_LENGTH; + + /** value for enable/disable password validation */ + private boolean isPasswordValidateEnabled = DEFAULT_PASSWORD_VALIDATE_ENABLED; /** array of all lower case characters (used for calculating password entropy) */ private static final char[] CHARS_LOWER = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; @@ -62,7 +68,7 @@ /** array of all special characters (used for calculating password entropy) */ private static final char[] CHARS_SPECIAL = { '!', '$', '*', '+', '-', '.', '=', '?', '@', '^', '_', '|', '~' }; - + /** * Default zero-arg constructor * DO NOT USE @@ -89,61 +95,74 @@ serverConfigurationService = (ServerConfigurationService) ComponentManager.get(org.sakaiproject.component.api.ServerConfigurationService.class); } if (serverConfigurationService != null) { + isPasswordValidateEnabled = serverConfigurationService.getBoolean(SAK_PROP_PASSWORD_VALIDATE_ENABLED, DEFAULT_PASSWORD_VALIDATE_ENABLED); minEntropy = serverConfigurationService.getInt(SAK_PROP_MIN_PASSWORD_ENTROPY, minEntropy); - maxSequenceLength = serverConfigurationService.getInt(SAK_PROP_MAX_PASSWORD_SEQ_LENGTH, maxSequenceLength); + maxAllowedSequenceLength = serverConfigurationService.getInt(SAK_PROP_MAX_PASSWORD_SEQ_LENGTH, maxAllowedSequenceLength); + if (maxAllowedSequenceLength < 0) { + maxAllowedSequenceLength = 0; + } } - logger.info("PasswordPolicyProviderDefaultImpl.init(): minEntropy="+minEntropy+", maxSequenceLength="+maxSequenceLength); + logger.info("PasswordPolicyProviderDefaultImpl.init(): minEntropy="+minEntropy+", maxAllowedSequenceLength="+maxAllowedSequenceLength); } /** * Destroy method (Spring) */ public void destroy() { - if (logger.isDebugEnabled()) - logger.debug("PasswordPolicyProviderDefaultImpl.destroy()"); + if (logger.isDebugEnabled()) { + logger.debug("PasswordPolicyProviderDefaultImpl.destroy()"); + } } /** - * {@inheritDoc} - */ - public boolean validatePassword(String password, String userDisplayID) { - if (logger.isDebugEnabled()) - logger.debug("PasswordPolicyProviderDefaultImpl.validatePassword( " + password + " )"); + * {@inheritDoc} + */ + public boolean validatePassword( String password, String userDisplayID ) + { + if( logger.isDebugEnabled() ) { + logger.debug( "PasswordPolicyProviderDefaultImpl.validatePassword( " + password + " )" ); + } + + // If password validation is turned on, do the validation + if (isPasswordValidateEnabled) + { + // If the password is null, it's invalid + if( password == null ) { + return false; + } - // If the password is null, it's invalid - if (password == null) { - return false; // SHORT CIRCUIT - } - - // If the password contains X number of characters from their display ID, it's invalid - // (where X is the maximum password sequence length defined in sakai.properties) - if (userDisplayID != null) { - int length = userDisplayID.length(); - for (int i = 0; i < length - (maxSequenceLength - 1); i++) { - String sub = userDisplayID.substring(i, i + maxSequenceLength); - if (password.indexOf(sub) > -1) { - return false; // SHORT CIRCUIT + // If the password contains more than X number of characters from their display ID, it's invalid + // (where X is the maximum allowed password sequence length defined in sakai.properties) + if( userDisplayID != null ) + { + int length = userDisplayID.length(); + for( int i = 0; i < length - maxAllowedSequenceLength; i++ ) + { + String sub = userDisplayID.substring( i, i + (maxAllowedSequenceLength + 1) ); + if( password.indexOf( sub ) > -1 ) { + return false; + } } } - } - // Count the number of character sets used in the password - int characterSets = 0; - characterSets += isCharacterSetPresentInPassword(CHARS_LOWER, password); - characterSets += isCharacterSetPresentInPassword(CHARS_UPPER, password); - characterSets += isCharacterSetPresentInPassword(CHARS_DIGIT, password); - characterSets += isCharacterSetPresentInPassword(CHARS_SPECIAL, password); + // Count the number of character sets used in the password + int characterSets = 0; + characterSets += isCharacterSetPresentInPassword( CHARS_LOWER, password ); + characterSets += isCharacterSetPresentInPassword( CHARS_UPPER, password ); + characterSets += isCharacterSetPresentInPassword( CHARS_DIGIT, password ); + characterSets += isCharacterSetPresentInPassword( CHARS_SPECIAL, password ); - // Calculate and verify the password strength - int strength = password.length() * characterSets; - if (strength < minEntropy) { - return false; // SHORT CIRCUIT + // Calculate and verify the password strength + int strength = password.length() * characterSets; + if( strength < minEntropy ) { + return false; + } } + + // The password has passed all requirements, OR password validation is turned off; therefore the password is valid + return true; + } - // The password has passed all requirements, therefore the password is valid - return true; - } - /** * Determine if the given character set is present in the given password string. * @@ -165,11 +184,22 @@ /** * {@inheritDoc} */ - public String getClientValidatePasswordFunction() { - if (logger.isDebugEnabled()) - logger.debug("PasswordPolicyProviderDefaultImpl.getClientValidatePasswordFunction()"); - String javaScript = ""; - return javaScript; + public String getJSONForClientSideValidatePasswordFunction() { + return "{ maxSeqLength: " + maxAllowedSequenceLength + ", minStrength: " + minEntropy + " }"; } + + /** + * {@inheritDoc} + */ + public String getPathToClientSidePasswordPolicyJavaScript() { + return "/library/js/passwordPolicy.js"; + } + /** + * {@inheritDoc} + */ + public boolean isPasswordValidationEnabled() + { + return isPasswordValidateEnabled; + } } Index: api/src/main/java/org/sakaiproject/user/api/PasswordPolicyProvider.java =================================================================== --- api/src/main/java/org/sakaiproject/user/api/PasswordPolicyProvider.java (revision 130087) +++ api/src/main/java/org/sakaiproject/user/api/PasswordPolicyProvider.java (working copy) @@ -25,40 +25,69 @@ * This interface provides the method stubs needed for any password policy object. * All password policy implementations need to implement this interface. * + * @author bjones86 + * * https://jira.sakaiproject.org/browse/KNL-1123 + * https://jira.sakaiproject.org/browse/RES-54 */ public interface PasswordPolicyProvider { - /** value for minimum password entropy */ + /** default value for minimum password entropy */ public static final int DEFAULT_MIN_ENTROPY = 16; - /** value for maximum password sequence length */ - public static final int DEFAULT_MAX_SEQ_LENGTH = 3; + /** default value for maximum allowed password sequence length */ + public static final int DEFAULT_MAX_ALLOWED_SEQ_LENGTH = 3; + + /** default value for enable/disable password validation */ + public static final boolean DEFAULT_PASSWORD_VALIDATE_ENABLED = true; /** sakai.property for minimum password entropy */ public static final String SAK_PROP_MIN_PASSWORD_ENTROPY = "user.password.minimum.entropy"; - /** sakai.property for maximum password sequence length */ + /** sakai.property for maximum allowed password sequence length */ public static final String SAK_PROP_MAX_PASSWORD_SEQ_LENGTH = "user.password.maximum.sequence.length"; /** sakai.property for minimum password entropy */ public static final String SAK_PROP_PROVIDER_NAME = "user.password.policy.provider.name"; + + /** sakai.property for enable/disable password validation */ + public static final String SAK_PROP_PASSWORD_VALIDATE_ENABLED = "user.password.validate.enabled"; /** - * This function returns a boolean value of true/false, depending on if the given password meets the validation criteria. - * - * Based on verifyPasswordStrength() in http://grepcode.com/file/repo1.maven.org/maven2/org.owasp.esapi/esapi/2.0_rc10/org/owasp/esapi/reference/FileBasedAuthenticator.java - * - * @param password the password to be validated - * @param userDisplayID the user's login ID - * @return true/false (password is valid/invalid) - */ - public boolean validatePassword(String password, String userDisplayID); + * This function returns a boolean value of true/false, depending + * on if the given password meets the validation criteria. + * + * Based on verifyPasswordStrength() in + * http://grepcode.com/file/repo1.maven.org/maven2/org.owasp.esapi/esapi/2.0_rc10/org/owasp/esapi/reference/FileBasedAuthenticator.java + * + * @param password + * the password to be validated + * @param userDisplayID + * the user's login ID + * @return true/false (password is valid/invalid) + */ + public boolean validatePassword( String password, String userDisplayID ); /** - * This method is called to retrieve the JavaScript validation function to be used client-side. + * This method is called to retrieve the JSON data needed by the client side validate password function + * + * @return a JSON object loaded with parameters for the client side validation function to use + */ + public String getJSONForClientSideValidatePasswordFunction(); + + /** + * This method is called to retrive the location of the JavaScript file that contains + * the client side validation function + * + * @return the path to the JavaScript file + */ + public String getPathToClientSidePasswordPolicyJavaScript(); + + /** + * This method is called to determine if the password validation is enabled/disabled in sakai.properties. + * Used for client-side JavaScript to determine whether or not to run the validation algorithm. * - * @return the JavaScript function to be used client-side + * @return true/false (enabled/disabled) */ - public String getClientValidatePasswordFunction(); + public boolean isPasswordValidationEnabled(); }