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.List;
034 import java.util.Set;
035
036 import org.opends.server.admin.server.ConfigurationChangeListener;
037 import org.opends.server.admin.std.server.
038 RepeatedCharactersPasswordValidatorCfg;
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 are not allowed to have the same character appear
053 * several times consecutively.
054 */
055 public class RepeatedCharactersPasswordValidator
056 extends PasswordValidator<RepeatedCharactersPasswordValidatorCfg>
057 implements ConfigurationChangeListener<
058 RepeatedCharactersPasswordValidatorCfg>
059 {
060 // The current configuration for this password validator.
061 private RepeatedCharactersPasswordValidatorCfg currentConfig;
062
063
064
065 /**
066 * Creates a new instance of this repeated characters password validator.
067 */
068 public RepeatedCharactersPasswordValidator()
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 RepeatedCharactersPasswordValidatorCfg configuration)
084 {
085 configuration.addRepeatedCharactersChangeListener(this);
086 currentConfig = configuration;
087 }
088
089
090
091 /**
092 * {@inheritDoc}
093 */
094 @Override()
095 public void finalizePasswordValidator()
096 {
097 currentConfig.removeRepeatedCharactersChangeListener(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 repeated characters in the password.
113 RepeatedCharactersPasswordValidatorCfg config = currentConfig;
114 int maxRepeats = config.getMaxConsecutiveLength();
115 if (maxRepeats <= 0)
116 {
117 // We don't need to check anything, so the password will be acceptable.
118 return true;
119 }
120
121
122 // Get the password as a string. If we should use case-insensitive
123 // validation, then convert it to use all lowercase characters.
124 String passwordString = newPassword.stringValue();
125 if (! config.isCaseSensitiveValidation())
126 {
127 passwordString = passwordString.toLowerCase();
128 }
129
130
131 // Create variables to keep track of the last character we've seen and how
132 // many times we have seen it.
133 char lastCharacter = '\u0000';
134 int consecutiveCount = 0;
135
136
137 // Iterate through the characters in the password. If the consecutive
138 // count ever gets too high, then fail.
139 for (int i=0; i < passwordString.length(); i++)
140 {
141 char currentCharacter = passwordString.charAt(i);
142 if (currentCharacter == lastCharacter)
143 {
144 consecutiveCount++;
145 if (consecutiveCount > maxRepeats)
146 {
147 Message message =
148 ERR_REPEATEDCHARS_VALIDATOR_TOO_MANY_CONSECUTIVE.get(
149 maxRepeats);
150 invalidReason.append(message);
151 return false;
152 }
153 }
154 else
155 {
156 lastCharacter = currentCharacter;
157 consecutiveCount = 1;
158 }
159 }
160
161 return true;
162 }
163
164
165
166 /**
167 * {@inheritDoc}
168 */
169 public boolean isConfigurationChangeAcceptable(
170 RepeatedCharactersPasswordValidatorCfg configuration,
171 List<Message> unacceptableReasons)
172 {
173 // All of the necessary validation should have been performed automatically,
174 // so if we get to this point then the new configuration will be acceptable.
175 return true;
176 }
177
178
179
180 /**
181 * {@inheritDoc}
182 */
183 public ConfigChangeResult applyConfigurationChange(
184 RepeatedCharactersPasswordValidatorCfg configuration)
185 {
186 ResultCode resultCode = ResultCode.SUCCESS;
187 boolean adminActionRequired = false;
188 ArrayList<Message> messages = new ArrayList<Message>();
189
190 // For this password validator, we will always be able to successfully apply
191 // the new configuration.
192 currentConfig = configuration;
193
194 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
195 }
196 }
197