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