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    import java.util.List;
031    import java.util.Set;
032    
033    import org.opends.server.api.PasswordValidator;
034    import org.opends.server.config.ConfigException;
035    import org.opends.server.types.ByteString;
036    import org.opends.server.types.ByteStringFactory;
037    import org.opends.server.types.ConfigChangeResult;
038    import org.opends.server.types.Entry;
039    import org.opends.server.types.InitializationException;
040    import org.opends.server.types.Operation;
041    import org.opends.server.types.ResultCode;
042    import org.opends.server.util.LevenshteinDistance;
043    import org.opends.server.admin.std.server.SimilarityBasedPasswordValidatorCfg;
044    import org.opends.server.admin.server.ConfigurationChangeListener;
045    
046    import static org.opends.messages.ExtensionMessages.*;
047    import org.opends.messages.MessageBuilder;
048    
049    
050    /**
051     * This class provides a password validator that can ensure that the provided
052     * password meets minimum similarity requirements.
053     */
054    public class SimilarityBasedPasswordValidator extends
055        PasswordValidator<SimilarityBasedPasswordValidatorCfg> implements
056        ConfigurationChangeListener<SimilarityBasedPasswordValidatorCfg>
057    {
058    
059      // The current configuration for this password validator.
060      private SimilarityBasedPasswordValidatorCfg currentConfig;
061    
062    
063      /**
064       * Creates a new instance of this password validator.
065       */
066      public SimilarityBasedPasswordValidator()
067      {
068        super();
069    
070    
071        // All initialization must be done in the initializePasswordValidator
072        // method.
073      }
074    
075      /**
076       * {@inheritDoc}
077       */
078      @Override()
079      public void initializePasswordValidator(
080                       SimilarityBasedPasswordValidatorCfg configuration)
081             throws ConfigException, InitializationException
082      {
083        configuration.addSimilarityBasedChangeListener(this);
084    
085        currentConfig = configuration;
086      }
087    
088      /**
089       * {@inheritDoc}
090       */
091      @Override()
092      public void finalizePasswordValidator()
093      {
094        currentConfig.removeSimilarityBasedChangeListener(this);
095      }
096    
097    
098    
099      /**
100       * {@inheritDoc}
101       */
102      @Override()
103      public boolean passwordIsAcceptable(ByteString newPassword,
104                                          Set<ByteString> currentPasswords,
105                                          Operation operation, Entry userEntry,
106                                          MessageBuilder invalidReason)  {
107    
108        int minDifference = currentConfig.getMinPasswordDifference();
109        ByteString passwd = newPassword == null
110                            ? ByteStringFactory.create("")
111                            : newPassword;
112    
113        if (currentPasswords == null || currentPasswords.size() == 0) {
114          // This validator requires access to at least one current password.
115          // If we don't have a current password, then we can't validate it, so
116          // we'll have to assume it is OK.  Ideally, the password policy should be
117          // configured to always require the current password, but even then the
118          // current password probably won't be availble during an administrative
119          // password reset.
120          return true;
121        }
122    
123        for (ByteString bs : currentPasswords) {
124            if (bs == null) {
125                continue;
126            }
127            int ldistance = LevenshteinDistance.calculate(passwd.stringValue(),
128                                                          bs.stringValue());
129            if (ldistance < minDifference) {
130              invalidReason.append(ERR_PWDIFFERENCEVALIDATOR_TOO_SMALL.get(
131                      minDifference));
132              return false;
133            }
134        }
135    
136        return true;
137      }
138    
139      /**
140       * {@inheritDoc}
141       */
142      public boolean isConfigurationChangeAcceptable(
143                          SimilarityBasedPasswordValidatorCfg configuration,
144                          List<Message> unacceptableReasons)
145      {
146        return true;
147      }
148    
149      /**
150       * {@inheritDoc}
151       */
152      public ConfigChangeResult applyConfigurationChange(
153                  SimilarityBasedPasswordValidatorCfg configuration)
154      {
155        currentConfig = configuration;
156        return new ConfigChangeResult(ResultCode.SUCCESS, false);
157      }
158    }
159