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