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-2016 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.forgerock.opendj.config.server.ConfigurationChangeListener; 030import org.forgerock.opendj.server.config.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 /** Creates a new instance of this unique characters password validator. */ 048 public UniqueCharactersPasswordValidator() 049 { 050 super(); 051 052 // No implementation is required here. All initialization should be 053 // performed in the initializePasswordValidator() method. 054 } 055 056 @Override 057 public void initializePasswordValidator( 058 UniqueCharactersPasswordValidatorCfg configuration) 059 { 060 configuration.addUniqueCharactersChangeListener(this); 061 currentConfig = configuration; 062 } 063 064 @Override 065 public void finalizePasswordValidator() 066 { 067 currentConfig.removeUniqueCharactersChangeListener(this); 068 } 069 070 @Override 071 public boolean passwordIsAcceptable(ByteString newPassword, 072 Set<ByteString> currentPasswords, 073 Operation operation, Entry userEntry, 074 LocalizableMessageBuilder invalidReason) 075 { 076 // Get a handle to the current configuration and see if we need to count 077 // the number of unique characters in the password. 078 UniqueCharactersPasswordValidatorCfg config = currentConfig; 079 int minUniqueCharacters = config.getMinUniqueCharacters(); 080 if (minUniqueCharacters <= 0) 081 { 082 // We don't need to check anything, so the password will be acceptable. 083 return true; 084 } 085 086 // Create a set that will be used to keep track of the unique characters 087 // contained in the proposed password. 088 HashSet<Character> passwordCharacters = new HashSet<>(); 089 090 // Iterate through the characters in the new password and place them in the 091 // set as needed. If we should behave in a case-insensitive manner, then 092 // convert all the characters to lowercase first. 093 String passwordString = newPassword.toString(); 094 if (! config.isCaseSensitiveValidation()) 095 { 096 passwordString = passwordString.toLowerCase(); 097 } 098 099 for (int i=0; i < passwordString.length(); i++) 100 { 101 passwordCharacters.add(passwordString.charAt(i)); 102 } 103 104 // If the size of the password characters set is less than the minimum 105 // number of allowed unique characters, then we will reject the password. 106 if (passwordCharacters.size() < minUniqueCharacters) 107 { 108 LocalizableMessage message = ERR_UNIQUECHARS_VALIDATOR_NOT_ENOUGH_UNIQUE_CHARS.get( 109 minUniqueCharacters); 110 invalidReason.append(message); 111 return false; 112 } 113 114 return true; 115 } 116 117 @Override 118 public boolean isConfigurationChangeAcceptable( 119 UniqueCharactersPasswordValidatorCfg configuration, 120 List<LocalizableMessage> unacceptableReasons) 121 { 122 // All of the necessary validation should have been performed automatically, 123 // so if we get to this point then the new configuration will be acceptable. 124 return true; 125 } 126 127 @Override 128 public ConfigChangeResult applyConfigurationChange( 129 UniqueCharactersPasswordValidatorCfg configuration) 130 { 131 final ConfigChangeResult ccr = new ConfigChangeResult(); 132 133 // For this password validator, we will always be able to successfully apply 134 // the new configuration. 135 currentConfig = configuration; 136 137 return ccr; 138 } 139}