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