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