001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2015 ForgeRock AS. 016 */ 017package org.opends.server.extensions; 018 019import static org.opends.messages.ExtensionMessages.*; 020 021import java.util.HashSet; 022import java.util.List; 023import java.util.Set; 024 025import org.forgerock.opendj.config.server.ConfigChangeResult; 026import org.forgerock.opendj.ldap.ByteString; 027import org.forgerock.i18n.LocalizableMessage; 028import org.forgerock.i18n.LocalizableMessageBuilder; 029import org.opends.server.admin.server.ConfigurationChangeListener; 030import org.opends.server.admin.std.server.UniqueCharactersPasswordValidatorCfg; 031import org.opends.server.api.PasswordValidator; 032import org.opends.server.types.*; 033 034/** 035 * This class provides an OpenDS password validator that may be used to ensure 036 * that proposed passwords contain at least a specified number of different 037 * characters. 038 */ 039public class UniqueCharactersPasswordValidator 040 extends PasswordValidator<UniqueCharactersPasswordValidatorCfg> 041 implements ConfigurationChangeListener< 042 UniqueCharactersPasswordValidatorCfg> 043{ 044 /** The current configuration for this password validator. */ 045 private UniqueCharactersPasswordValidatorCfg currentConfig; 046 047 048 049 /** 050 * Creates a new instance of this unique characters password validator. 051 */ 052 public UniqueCharactersPasswordValidator() 053 { 054 super(); 055 056 // No implementation is required here. All initialization should be 057 // performed in the initializePasswordValidator() method. 058 } 059 060 061 062 /** {@inheritDoc} */ 063 @Override 064 public void initializePasswordValidator( 065 UniqueCharactersPasswordValidatorCfg configuration) 066 { 067 configuration.addUniqueCharactersChangeListener(this); 068 currentConfig = configuration; 069 } 070 071 072 073 /** {@inheritDoc} */ 074 @Override 075 public void finalizePasswordValidator() 076 { 077 currentConfig.removeUniqueCharactersChangeListener(this); 078 } 079 080 081 082 /** {@inheritDoc} */ 083 @Override 084 public boolean passwordIsAcceptable(ByteString newPassword, 085 Set<ByteString> currentPasswords, 086 Operation operation, Entry userEntry, 087 LocalizableMessageBuilder invalidReason) 088 { 089 // Get a handle to the current configuration and see if we need to count 090 // the number of unique characters in the password. 091 UniqueCharactersPasswordValidatorCfg config = currentConfig; 092 int minUniqueCharacters = config.getMinUniqueCharacters(); 093 if (minUniqueCharacters <= 0) 094 { 095 // We don't need to check anything, so the password will be acceptable. 096 return true; 097 } 098 099 100 101 // Create a set that will be used to keep track of the unique characters 102 // contained in the proposed password. 103 HashSet<Character> passwordCharacters = new HashSet<>(); 104 105 // Iterate through the characters in the new password and place them in the 106 // set as needed. If we should behave in a case-insensitive manner, then 107 // convert all the characters to lowercase first. 108 String passwordString = newPassword.toString(); 109 if (! config.isCaseSensitiveValidation()) 110 { 111 passwordString = passwordString.toLowerCase(); 112 } 113 114 for (int i=0; i < passwordString.length(); i++) 115 { 116 passwordCharacters.add(passwordString.charAt(i)); 117 } 118 119 // If the size of the password characters set is less than the minimum 120 // number of allowed unique characters, then we will reject the password. 121 if (passwordCharacters.size() < minUniqueCharacters) 122 { 123 LocalizableMessage message = ERR_UNIQUECHARS_VALIDATOR_NOT_ENOUGH_UNIQUE_CHARS.get( 124 minUniqueCharacters); 125 invalidReason.append(message); 126 return false; 127 } 128 129 return true; 130 } 131 132 133 134 /** {@inheritDoc} */ 135 public boolean isConfigurationChangeAcceptable( 136 UniqueCharactersPasswordValidatorCfg configuration, 137 List<LocalizableMessage> unacceptableReasons) 138 { 139 // All of the necessary validation should have been performed automatically, 140 // so if we get to this point then the new configuration will be acceptable. 141 return true; 142 } 143 144 145 146 /** {@inheritDoc} */ 147 public ConfigChangeResult applyConfigurationChange( 148 UniqueCharactersPasswordValidatorCfg configuration) 149 { 150 final ConfigChangeResult ccr = new ConfigChangeResult(); 151 152 // For this password validator, we will always be able to successfully apply 153 // the new configuration. 154 currentConfig = configuration; 155 156 return ccr; 157 } 158} 159