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-2013 UnboundID Corp.
026     */
027    package com.unboundid.directory.sdk.ds.api;
028    
029    
030    
031    import java.util.Collections;
032    import java.util.List;
033    import java.util.Map;
034    import java.util.Set;
035    
036    import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
037    import com.unboundid.directory.sdk.common.internal.Reconfigurable;
038    import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
039    import com.unboundid.directory.sdk.common.types.Entry;
040    import com.unboundid.directory.sdk.common.types.OperationContext;
041    import com.unboundid.directory.sdk.ds.config.PasswordValidatorConfig;
042    import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
043    import com.unboundid.directory.sdk.ds.types.DirectoryServerContext;
044    import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
045    import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
046    import com.unboundid.ldap.sdk.LDAPException;
047    import com.unboundid.ldap.sdk.ResultCode;
048    import com.unboundid.util.ByteString;
049    import com.unboundid.util.Extensible;
050    import com.unboundid.util.ThreadSafety;
051    import com.unboundid.util.ThreadSafetyLevel;
052    import com.unboundid.util.args.ArgumentException;
053    import 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)
098    public 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    }