All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sakaiproject.user.impl.PasswordPolicyProviderDefaultImpl Maven / Gradle / Ivy

There is a newer version: 23.3
Show newest version
/**********************************************************************************
 * $URL$
 * $Id$
 ***********************************************************************************
 *
 * Copyright (c) 2005, 2006, 2008, 2009, 2010 Sakai Foundation
 *
 * Licensed under the Educational Community 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.opensource.org/licenses/ECL-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.sakaiproject.user.impl;

import java.util.Arrays;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.component.cover.ComponentManager;
import org.sakaiproject.component.api.ServerConfigurationService;
import org.sakaiproject.user.api.PasswordPolicyProvider;
import org.sakaiproject.user.api.User;
import org.sakaiproject.user.api.UserDirectoryService.PasswordRating;

/**
 * This is the default implementation of the Password policy provider.
 * 
 * https://jira.sakaiproject.org/browse/KNL-1123
 */
public class PasswordPolicyProviderDefaultImpl implements PasswordPolicyProvider {

    /** Our log (commons). */
    private static Log logger = LogFactory.getLog(PasswordPolicyProviderDefaultImpl.class);

    /** value for minimum password entropy */
    private static final int DEFAULT_MIN_ENTROPY = 16;
    
    /** value for medium password entropy multiplier */
    private static final int DEFAULT_MEDIUM_ENTROPY = 32;
    
    /** value for high password entropy multiplier */
    private static final int DEFAULT_HIGH_ENTROPY = 48;

    /** value for maximum password sequence length */
    private static final int DEFAULT_MAX_SEQ_LENGTH = 3;

    /** sakai.property for minimum password entropy */
    private static final String SAK_PROP_MIN_PASSWORD_ENTROPY = "user.password.minimum.entropy";
    
    /** sakai.property for medium password entropy multiplier */
    private static final String SAK_PROP_MEDIUM_PASSWORD_ENTROPY = "user.password.medium.entropy";
    
    /** sakai.property for high password entropy multiplier */
    private static final String SAK_PROP_HIGH_PASSWORD_ENTROPY = "user.password.high.entropy";

    /** sakai.property for maximum password sequence length */
    private static final String SAK_PROP_MAX_PASSWORD_SEQ_LENGTH = "user.password.maximum.sequence.length";

    /** 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' };

    /** array of all upper case characters (used for calculating password entropy) */
    private static final char[] CHARS_UPPER = { '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' };

    /** array of all digit characters (used for calculating password entropy) */
    private static final char[] CHARS_DIGIT = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

    /** array of all special characters (used for calculating password entropy) */
    private static final char[] CHARS_SPECIAL = { '!', '$', '*', '+', '-', '.', '=', '?', '@', '^', '_', '|', '~' };

    /** value for minimum password entropy */
    private int minEntropy = DEFAULT_MIN_ENTROPY;
    
    /** value for medium password entropy multiplier */
    private int mediumEntropy = DEFAULT_MEDIUM_ENTROPY;
    
    /** value for high password entropy multiplier */
    private int highEntropy = DEFAULT_HIGH_ENTROPY;

    /** value for maximum password sequence length */
    private int maxSequenceLength = DEFAULT_MAX_SEQ_LENGTH;

    /**
     * Default zero-arg constructor
     * DO NOT USE
     */
    PasswordPolicyProviderDefaultImpl() {
        this(null);
    }

    /**
     * @param serverConfigurationService
     */
    public PasswordPolicyProviderDefaultImpl(ServerConfigurationService serverConfigurationService) {
        this.serverConfigurationService = serverConfigurationService;
        init();
    }


    /**
     * Initialization method (Spring)
     */
    public void init() {
        // Get the values from sakai.properties
        if (serverConfigurationService == null) {
            serverConfigurationService = (ServerConfigurationService) ComponentManager.get(org.sakaiproject.component.api.ServerConfigurationService.class);
        }
        if (serverConfigurationService != null) {
            minEntropy = serverConfigurationService.getInt(SAK_PROP_MIN_PASSWORD_ENTROPY, minEntropy);
            mediumEntropy = serverConfigurationService.getInt(SAK_PROP_MEDIUM_PASSWORD_ENTROPY, mediumEntropy);
            highEntropy = serverConfigurationService.getInt(SAK_PROP_HIGH_PASSWORD_ENTROPY, highEntropy);
            maxSequenceLength = serverConfigurationService.getInt(SAK_PROP_MAX_PASSWORD_SEQ_LENGTH, maxSequenceLength);
            if (maxSequenceLength < 0) {
            	maxSequenceLength = 0;
            }
            if (mediumEntropy < minEntropy) {
            	mediumEntropy = DEFAULT_MEDIUM_ENTROPY;
            }
            if (highEntropy < mediumEntropy) {
            	highEntropy = DEFAULT_HIGH_ENTROPY;
            }
        }
        logger.info("PasswordPolicyProviderDefaultImpl.init(): minEntropy="+minEntropy+", mediumEntropy="+mediumEntropy+
        		", highEntropy="+highEntropy+", maxSequenceLength="+maxSequenceLength);
    }

    /**
     * Destroy method (Spring)
     */
    public void destroy() {
        if (logger.isDebugEnabled())
            logger.debug("PasswordPolicyProviderDefaultImpl.destroy()");
    }

    public PasswordRating validatePassword(String password, User user) {
        if (logger.isDebugEnabled())
            logger.debug("PasswordPolicyProviderDefaultImpl.validatePassword( " + password + " )");

        // If the password is null, it's invalid
        if (password == null) {
            return PasswordRating.FAILED; // 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 (user != null) {
            String userDisplayID = user.getDisplayId();
            if (userDisplayID != null) {
                int length = userDisplayID.length();
                for (int i = 0; i < length - maxSequenceLength; i++) {
                    String sub = userDisplayID.substring(i, i + (maxSequenceLength + 1));
                    if (password.indexOf(sub) > -1) {
                        return PasswordRating.FAILED; // SHORT CIRCUIT
                    }
                }
            }
        }

        // 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 PasswordRating.FAILED; // SHORT CIRCUIT
        }

        // The password has passed all requirements; determine the strength of the password and return the appropriate flag
        if (strength >= highEntropy) {
        	return PasswordRating.STRONG;
        }
        else if (strength >= mediumEntropy) {
        	return PasswordRating.MODERATE;
        }
        else {
        	return PasswordRating.WEAK;
        }
    }

    /**
     * Determine if the given character set is present in the given password string.
     * 
     * @param characterSet
     *            the set of characters to check for
     * @param password
     *            the password to search for the charachter set in
     * @return 1 if the character set is present in the password, 0 otherwise
     */
    private int isCharacterSetPresentInPassword(char[] characterSet, String password) {
        for (int i = 0; i < password.length(); i++) {
            if (Arrays.binarySearch(characterSet, password.charAt(i)) >= 0) {
                return 1; // SHORT CIRCUIT
            }
        }
        return 0;
    }


    private ServerConfigurationService serverConfigurationService;
    public void setServerConfigurationService(ServerConfigurationService serverConfigurationService) {
        this.serverConfigurationService = serverConfigurationService;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy