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 * docs/licenses/cddl.txt
011 * or http://www.opensource.org/licenses/cddl1.php.
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 * docs/licenses/cddl.txt.  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 2010-2015 UnboundID Corp.
026 */
027package com.unboundid.directory.sdk.ds.api;
028
029
030
031import java.util.Collections;
032import java.util.List;
033import java.util.Map;
034import java.util.Set;
035
036import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
037import com.unboundid.directory.sdk.common.internal.Reconfigurable;
038import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
039import com.unboundid.directory.sdk.common.types.Entry;
040import com.unboundid.directory.sdk.common.types.OperationContext;
041import com.unboundid.directory.sdk.ds.config.PasswordValidatorConfig;
042import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
043import com.unboundid.directory.sdk.ds.types.DirectoryServerContext;
044import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
045import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
046import com.unboundid.ldap.sdk.LDAPException;
047import com.unboundid.ldap.sdk.ResultCode;
048import com.unboundid.util.ByteString;
049import com.unboundid.util.Extensible;
050import com.unboundid.util.ThreadSafety;
051import com.unboundid.util.ThreadSafetyLevel;
052import com.unboundid.util.args.ArgumentException;
053import com.unboundid.util.args.ArgumentParser;
054
055
056
057/**
058 * This class defines an API that must be implemented by extensions which
059 * attempt to determine whether a proposed user password is acceptable.  Each
060 * server password policy may be configured with zero or more password
061 * validators, and whenever a user changes his or her password (and optionally
062 * whenever an administrator resets the password for another user), then each of
063 * the password validators configured in the password policy for the target user
064 * will be given access to the clear-text password in order to determine whether
065 * that password will be allowed.  Password validators will also have access to
066 * the rest of the user entry, and may also have access to a clear-text version
067 * of the user's current password(s) if they were provided in the request.
068 * <BR>
069 * <H2>Configuring Password Validators</H2>
070 * In order to configure a password validator created using this API, use a
071 * command like:
072 * <PRE>
073 *      dsconfig create-password-validator \
074 *           --validator-name "<I>{validator-name}</I>" \
075 *           --type third-party \
076 *           --set enabled:true \
077 *           --set "extension-class:<I>{class-name}</I>" \
078 *           --set "extension-argument:<I>{name=value}</I>"
079 * </PRE>
080 * where "<I>{validator-name}</I>" is the name to use for the password validator
081 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Java class
082 * that extends {@code com.unboundid.directory.sdk.ds.api.PasswordValidator},
083 * and "<I>{name=value}</I>" represents name-value pairs for any arguments to
084 * provide to the password validator.  If multiple arguments should be provided
085 * to the password validator, then the
086 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be
087 * provided multiple times.
088 *
089 * @see  com.unboundid.directory.sdk.ds.scripting.ScriptedPasswordValidator
090 */
091@Extensible()
092@DirectoryServerExtension()
093@DirectoryProxyServerExtension(appliesToLocalContent=true,
094     appliesToRemoteContent=false)
095@SynchronizationServerExtension(appliesToLocalContent=true,
096     appliesToSynchronizedContent=false)
097@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
098public abstract class PasswordValidator
099       implements UnboundIDExtension, Reconfigurable<PasswordValidatorConfig>,
100                  ExampleUsageProvider
101{
102  /**
103   * Creates a new instance of this password validator.  All password validator
104   * implementations must include a default constructor, but any initialization
105   * should generally be done in the {@code initializePasswordValidator} method.
106   */
107  public PasswordValidator()
108  {
109    // No implementation is required.
110  }
111
112
113
114  /**
115   * {@inheritDoc}
116   */
117  public abstract String getExtensionName();
118
119
120
121  /**
122   * {@inheritDoc}
123   */
124  public abstract String[] getExtensionDescription();
125
126
127
128  /**
129   * {@inheritDoc}
130   */
131  public void defineConfigArguments(final ArgumentParser parser)
132         throws ArgumentException
133  {
134    // No arguments will be allowed by default.
135  }
136
137
138
139  /**
140   * Initializes this password validator.
141   *
142   * @param  serverContext  A handle to the server context for the server in
143   *                        which this extension is running.
144   * @param  config         The general configuration for this password
145   *                        validator.
146   * @param  parser         The argument parser which has been initialized from
147   *                        the configuration for this password validator.
148   *
149   * @throws  LDAPException  If a problem occurs while initializing this
150   *                         password validator.
151   */
152  public void initializePasswordValidator(
153                   final DirectoryServerContext serverContext,
154                   final PasswordValidatorConfig config,
155                   final ArgumentParser parser)
156         throws LDAPException
157  {
158    // No initialization will be performed by default.
159  }
160
161
162
163  /**
164   * {@inheritDoc}
165   */
166  public boolean isConfigurationAcceptable(final PasswordValidatorConfig config,
167                      final ArgumentParser parser,
168                      final List<String> unacceptableReasons)
169  {
170    // No extended validation will be performed by default.
171    return true;
172  }
173
174
175
176  /**
177   * {@inheritDoc}
178   */
179  public ResultCode applyConfiguration(final PasswordValidatorConfig config,
180                                       final ArgumentParser parser,
181                                       final List<String> adminActionsRequired,
182                                       final List<String> messages)
183  {
184    // By default, no configuration changes will be applied.  If there are any
185    // arguments, then add an admin action message indicating that the extension
186    // needs to be restarted for any changes to take effect.
187    if (! parser.getNamedArguments().isEmpty())
188    {
189      adminActionsRequired.add(
190           "No configuration change has actually been applied.  The new " +
191                "configuration will not take effect until this password " +
192                "validator is disabled and re-enabled or until the server " +
193                "is restarted.");
194    }
195
196    return ResultCode.SUCCESS;
197  }
198
199
200
201  /**
202   * Performs any cleanup which may be necessary when this password validator is
203   * to be taken out of service.
204   */
205  public void finalizePasswordValidator()
206  {
207    // No implementation is required.
208  }
209
210
211
212  /**
213   * Indicates whether the proposed password is acceptable for the specified
214   * user.
215   *
216   * @param  operationContext  The operation context for the associated request.
217   *                           It may be associated with an add, modify, or
218   *                           password modify operation.
219   * @param  newPassword       The proposed new password for the user that
220   *                           should be validated.  It will not be encoded or
221   *                           obscured in any way.
222   * @param  currentPasswords  The current set of passwords for the user, if
223   *                           available.  It may be {@code null} if this is
224   *                           not available.  Note that even if one or more
225   *                           current passwords are available, it may not
226   *                           constitute the complete set of passwords for the
227   *                           user.
228   * @param  userEntry         The entry for the user whose password is being
229   *                           changed.
230   * @param  invalidReason     A buffer to which a message may be appended to
231   *                           indicate why the proposed password is not
232   *                           acceptable.
233   *
234   * @return  {@code true} if the proposed new password is acceptable, or
235   *          {@code false} if not.
236   */
237  public abstract boolean isPasswordAcceptable(
238                               final OperationContext operationContext,
239                               final ByteString newPassword,
240                               final Set<ByteString> currentPasswords,
241                               final Entry userEntry,
242                               final StringBuilder invalidReason);
243
244
245
246  /**
247   * {@inheritDoc}
248   */
249  public Map<List<String>,String> getExamplesArgumentSets()
250  {
251    return Collections.emptyMap();
252  }
253}