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