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 *      Portions Copyright 2013-2024 Ping Identity Corporation
026 */
027package com.unboundid.directory.sdk.ds.api;
028
029
030
031import java.util.Collections;
032import java.util.List;
033import java.util.Map;
034
035import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
036import com.unboundid.directory.sdk.common.internal.Reconfigurable;
037import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
038import com.unboundid.directory.sdk.common.types.Entry;
039import com.unboundid.directory.sdk.ds.config.PasswordStorageSchemeConfig;
040import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
041import com.unboundid.directory.sdk.ds.types.DirectoryServerContext;
042import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
043import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
044import com.unboundid.ldap.sdk.LDAPException;
045import com.unboundid.ldap.sdk.Modification;
046import com.unboundid.ldap.sdk.ResultCode;
047import com.unboundid.util.ByteString;
048import com.unboundid.util.Extensible;
049import com.unboundid.util.ThreadSafety;
050import com.unboundid.util.ThreadSafetyLevel;
051import com.unboundid.util.args.ArgumentException;
052import com.unboundid.util.args.ArgumentParser;
053
054
055
056/**
057 * This class defines an API that may be implemented by extensions which provide
058 * enhanced password storage scheme support.  Although the older
059 * {@link PasswordStorageScheme} API is still supported, this enhanced version
060 * is both simpler and provides additional functionality, including providing
061 * access to the user entry (which may be useful, for example, if salt or
062 * encryption information is to be derived from other content in the entry, or
063 * from another repository that can be accessed based on content in the entry).
064 * However, this API is generally simpler than the {@code PasswordStorageScheme}
065 * API, so it may be desirable for all new implementations to implement this API
066 * instead.
067 * <BR><BR>
068 * Encoded passwords may take one of two forms.  The first is the "user
069 * password" syntax, in which the encoded password is represented by the name of
070 * the storage scheme in curly braces followed by the transformed password
071 * (e.g., "{scheme}encoded").  This format isn't based on any defined standard,
072 * but is commonly used by a number of directory server implementations.  The
073 * second format is the authentication password syntax as described in RFC 3112,
074 * in which the encoded representation is broken into scheme, authInfo, and
075 * authValue segments separated by dollar signs (e.g.,
076 * "scheme$authInfo$authValue").  All password storage schemes are required to
077 * support the "user password" syntax and may optionally also support the
078 * authentication password syntax.
079 * <BR>
080 * <H2>Configuring Enhanced Password Storage Schemes</H2>
081 * In order to configure a password storage scheme created using this API, use
082 * a command like:
083 * <PRE>
084 *      dsconfig create-password-storage-scheme \
085 *           --scheme-name "<I>{scheme-name}</I>" \
086 *           --type third-party-enhanced \
087 *           --set enabled:true \
088 *           --set "extension-class:<I>{class-name}</I>" \
089 *           --set "extension-argument:<I>{name=value}</I>"
090 * </PRE>
091 * where "<I>{scheme-name}</I>" is the name to use for the password storage
092 * scheme instance, "<I>{class-name}</I>" is the fully-qualified name of the
093 * Java class that extends
094 * {@code com.unboundid.directory.sdk.ds.api.PasswordStorageScheme}, and
095 * "<I>{name=value}</I>" represents name-value pairs for any arguments to
096 * provide to the password storage scheme.  If multiple arguments should be
097 * provided to the password storage scheme, then the
098 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be
099 * provided multiple times.
100 *
101 * @see PasswordStorageScheme
102 */
103@Extensible()
104@DirectoryServerExtension()
105@DirectoryProxyServerExtension(appliesToLocalContent=true,
106     appliesToRemoteContent=false)
107@SynchronizationServerExtension(appliesToLocalContent=true,
108     appliesToSynchronizedContent=false)
109@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
110public abstract class EnhancedPasswordStorageScheme
111       implements UnboundIDExtension,
112                  Reconfigurable<PasswordStorageSchemeConfig>,
113                  ExampleUsageProvider
114{
115  /**
116   * Creates a new instance of this password storage scheme.  All password
117   * storage scheme implementations must include a default constructor, but any
118   * initialization should generally be done in the
119   * {@code initializePasswordStorageScheme} method.
120   */
121  public EnhancedPasswordStorageScheme()
122  {
123    // No implementation is required.
124  }
125
126
127
128  /**
129   * {@inheritDoc}
130   */
131  public abstract String getExtensionName();
132
133
134
135  /**
136   * {@inheritDoc}
137   */
138  public abstract String[] getExtensionDescription();
139
140
141
142  /**
143   * {@inheritDoc}
144   */
145  public void defineConfigArguments(final ArgumentParser parser)
146         throws ArgumentException
147  {
148    // No arguments will be allowed by default.
149  }
150
151
152
153  /**
154   * Initializes this password storage scheme.
155   *
156   * @param  serverContext  A handle to the server context for the server in
157   *                        which this extension is running.
158   * @param  config         The general configuration for this password storage
159   *                        scheme.
160   * @param  parser         The argument parser which has been initialized from
161   *                        the configuration for this password storage scheme.
162   *
163   * @throws  LDAPException  If a problem occurs while initializing this
164   *                         password storage scheme.
165   */
166  public void initializePasswordStorageScheme(
167                   final DirectoryServerContext serverContext,
168                   final PasswordStorageSchemeConfig config,
169                   final ArgumentParser parser)
170         throws LDAPException
171  {
172    // No initialization will be performed by default.
173  }
174
175
176
177  /**
178   * {@inheritDoc}
179   */
180  public boolean isConfigurationAcceptable(
181                      final PasswordStorageSchemeConfig config,
182                      final ArgumentParser parser,
183                      final List<String> unacceptableReasons)
184  {
185    // No extended validation will be performed by default.
186    return true;
187  }
188
189
190
191  /**
192   * {@inheritDoc}
193   */
194  public ResultCode applyConfiguration(final PasswordStorageSchemeConfig config,
195                                       final ArgumentParser parser,
196                                       final List<String> adminActionsRequired,
197                                       final List<String> messages)
198  {
199    // By default, no configuration changes will be applied.  If there are any
200    // arguments, then add an admin action message indicating that the extension
201    // needs to be restarted for any changes to take effect.
202    if (! parser.getNamedArguments().isEmpty())
203    {
204      adminActionsRequired.add(
205           "No configuration change has actually been applied.  The new " +
206                "configuration will not take effect until this password " +
207                "storage scheme is disabled and re-enabled or until the " +
208                "server is restarted.");
209    }
210
211    return ResultCode.SUCCESS;
212  }
213
214
215
216  /**
217   * Performs any cleanup which may be necessary when this password storage
218   * scheme is to be taken out of service.
219   */
220  public void finalizePasswordStorageScheme()
221  {
222    // No implementation is required.
223  }
224
225
226
227  /**
228   * Retrieves the name for this password storage scheme.  This will be the
229   * identifier which appears in curly braces at the beginning of the encoded
230   * password.  The name should not include curly braces.
231   *
232   * @return  The name for this password storage scheme.
233   */
234  public abstract String getStorageSchemeName();
235
236
237
238  /**
239   * Indicates whether this password storage scheme encodes passwords in a form
240   * that allows the original plaintext value to be obtained from the encoded
241   * representation.
242   *
243   * @return  {@code true} if the original plaintext password may be obtained
244   *          from the encoded password, or {@code false} if not.
245   */
246  public abstract boolean isReversible();
247
248
249
250  /**
251   * Indicates whether this password storage scheme encodes passwords in a form
252   * that may be considered secure.  A storage scheme should only be considered
253   * secure if it is not possible to trivially determine a clear-text value
254   * which may be used to generate a given encoded representation.
255   *
256   * @return  {@code true} if this password storage scheme may be considered
257   *          secure, or {@code false} if not.
258   */
259  public abstract boolean isSecure();
260
261
262
263  /**
264   * Indicates whether this password storage scheme requires access to the user
265   * entry in order to perform processing.  If the storage scheme does require
266   * access to the user entry (e.g., in order to obtain key or salt information
267   * from another attribute, or to use information in the entry to access
268   * information in another data store), then some features (e.g., the ability
269   * to perform extensible matching with password values, or the ability to use
270   * the encode-password tool to encode and compare passwords) may not be
271   * available.
272   * <BR><BR>
273   * Note that if this method returns {@code true}, then the server will never
274   * provide a {@code null} value for the {@code userEntry} parameter to methods
275   * that accept it.  However, if this method returns {@code false}, then there
276   * may be some instances in which the server may invoke those methods with a
277   * {@code null} value for the {@code userEntry} parameter (e.g., when invoked
278   * by the encode-password tool or when targeting a password with an extensible
279   * match search filter).
280   *
281   * @return  {@code true} if this password storage scheme requires access to
282   *          the user entry, or {@code false} if not.
283   */
284  public boolean requiresUserEntry()
285  {
286    return false;
287  }
288
289
290
291  /**
292   * Encodes the provided plaintext password for this storage scheme,
293   * without the name of the associated scheme.  Note that the
294   * provided plaintext password should not be altered in any way.
295   *
296   * @param  plaintext      The plaintext password to be encoded.  It must not
297   *                        be {@code null}.  Note that there is no guarantee
298   *                        that password validators have yet been invoked for
299   *                        this password, so this password storage scheme
300   *                        implementation should not make any assumptions about
301   *                        the format of the plaintext password or whether it
302   *                        will actually be allowed for use in the entry.
303   * @param  userEntry      The complete entry for the user for whom the
304   *                        password is to be encoded.  This will not be
305   *                        {@code null} for schemes in which
306   *                        {@link #requiresUserEntry} returns {@code true}.
307   * @param  modifications  The set of modifications to be applied to the user
308   *                        entry.  This will generally be non-{@code null} only
309   *                        for operations that use a modify to change the user
310   *                        password.  If a modification list is provided, then
311   *                        it will contain the complete set of modifications
312   *                        for the operation, some of which may have no
313   *                        impact on password encoding.
314   * @param  deterministic  Indicates whether the password encoding should be
315   *                        deterministic.  If this is {@code true}, then the
316   *                        scheme should attempt to generate a consistent
317   *                        encoding for the password (e.g., by determining the
318   *                        salt from a normalized representation of the user's
319   *                        DN).  This will be used during LDIF import
320   *                        processing (and potentially at other times) to help
321   *                        ensure that if the LDIF file is imported into
322   *                        multiple servers, any clear-text password
323   *                        encountered in the LDIF file will have the same
324   *                        encoded representation on all servers.  Some
325   *                        password storage schemes may choose to ignore this
326   *                        property if it does not apply or is not possible to
327   *                        achieve.
328   * @param  includeScheme  Indicates whether to include the name of the scheme
329   *                        in curly braces before the encoded password.
330   *
331   * @return  The password that has been encoded using this storage
332   *          scheme.
333   *
334   * @throws  LDAPException  If a problem occurs while attempting to encode the
335   *                         password.
336   */
337  public abstract ByteString encodePassword(final ByteString plaintext,
338                                  final Entry userEntry,
339                                  final List<Modification> modifications,
340                                  final boolean deterministic,
341                                  final boolean includeScheme)
342         throws LDAPException;
343
344
345
346  /**
347   * Indicates whether the provided plaintext password could have been used to
348   * generate the given encoded password.
349   *
350   * @param  plaintextPassword  The plaintext password provided by the user as
351   *                            part of a simple bind attempt.
352   * @param  storedPassword     The stored password to compare against the
353   *                            provided plaintext password.  It will not
354   *                            include the scheme name in curly braces.
355   * @param  userEntry          The complete entry for the user for whom the
356   *                            password is to be validated.  This will not be
357   *                            {@code null} for schemes in which
358   *                            {@link #requiresUserEntry} returns {@code true}.
359   *
360   * @return  {@code true} if the provided clear-text password could have been
361   *          used to generate the encoded password, or {@code false} if not.
362   */
363  public abstract boolean passwordMatches(final ByteString plaintextPassword,
364                                          final ByteString storedPassword,
365                                          final Entry userEntry);
366
367
368
369  /**
370   * Attempts to determine the plaintext password used to generate the provided
371   * encoded password.  This method should only be called if the
372   * {@link #isReversible} method returns {@code true}.
373   *
374   * @param  storedPassword  The password for which to obtain the plaintext
375   *                         value.  It should not include the scheme name in
376   *                         curly braces.
377   * @param  userEntry       The complete entry for the user for whom the
378   *                         password is to be validated.  This will not be
379   *                         {@code null} for schemes in which
380   *                         {@link #requiresUserEntry} returns {@code true}.
381   *
382   * @return  The plaintext password obtained from the given encoded password.
383   *
384   * @throws  LDAPException  If this password storage scheme is not reversible,
385   *                         or if the provided value could not be decoded to
386   *                         its plaintext representation.
387   */
388  public abstract ByteString getPlaintextValue(final ByteString storedPassword,
389                                               final Entry userEntry)
390         throws LDAPException;
391
392
393
394  /**
395   * Indicates whether this password storage scheme provides the ability to
396   * encode passwords in the authentication password syntax as described in RFC
397   * 3112.
398   *
399   * @return  {@code true} if this password storage scheme supports the
400   *          authentication password syntax, or {@code false} if not.
401   */
402  public boolean supportsAuthPasswordSyntax()
403  {
404    return false;
405  }
406
407
408
409  /**
410   * Retrieves the name that should be used to identify this password storage
411   * scheme when encoding passwords using the authentication password syntax as
412   * described in RFC 3112.  This should only be used if the
413   * {@link #supportsAuthPasswordSyntax} method returns {@code true}.
414   *
415   * @return  The name that should be used to identify this password storage
416   *          scheme when encoding passwords using the authentication password
417   *          syntax.
418   */
419  public String getAuthPasswordSchemeName()
420  {
421    return getStorageSchemeName();
422  }
423
424
425
426  /**
427   * Encodes the provided plaintext password using the authentication password
428   * syntax as defined in RFC 3112.  This should only be used if the
429   * {@link #supportsAuthPasswordSyntax} method returns {@code true}.
430   *
431   * @param  plaintext      The plaintext password to be encoded.  It must not
432   *                        be {@code null}.  Note that there is no guarantee
433   *                        that password validators have yet been invoked for
434   *                        this password, so this password storage scheme
435   *                        implementation should not make any assumptions about
436   *                        the format of the plaintext password or whether it
437   *                        will actually be allowed for use in the entry.
438   * @param  userEntry      The complete entry for the user for whom the
439   *                        password is to be encoded.  This will not be
440   *                        {@code null} for schemes in which
441   *                        {@link #requiresUserEntry} returns {@code true}.
442   * @param  modifications  The set of modifications to be applied to the user
443   *                        entry.  This will generally be non-{@code null} only
444   *                        for operations that use a modify to change the user
445   *                        password.  If a modification list is provided, then
446   *                        it will contain the complete set of modifications
447   *                        for the operation, some of which may have no
448   *                        impact on password encoding.
449   * @param  deterministic  Indicates whether the password encoding should be
450   *                        deterministic.  If this is {@code true}, then the
451   *                        scheme should attempt to generate a consistent
452   *                        encoding for the password (e.g., by determining the
453   *                        salt from a normalized representation of the user's
454   *                        DN).  This will be used during LDIF import
455   *                        processing (and potentially at other times) to help
456   *                        ensure that if the LDIF file is imported into
457   *                        multiple servers, any clear-text password
458   *                        encountered in the LDIF file will have the same
459   *                        encoded representation on all servers.  Some
460   *                        password storage schemes may choose to ignore this
461   *                        property if it does not apply or is not possible to
462   *                        achieve.
463   *
464   * @return  The encoded representation of the provided password.
465   *
466   * @throws  LDAPException  If a problem occurs while encoding the provided
467   *                         password, or if this password storage scheme does
468   *                         not support the authentication password syntax.
469   */
470  public ByteString encodeAuthPassword(final ByteString plaintext,
471                                       final Entry userEntry,
472                                       final List<Modification> modifications,
473                                       final boolean deterministic)
474         throws LDAPException
475  {
476    throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
477         "This password storage scheme does not support the use of the " +
478         "authentication password syntax.");
479  }
480
481
482
483  /**
484   * Indicates whether the provided plaintext password may be used to generate
485   * an encoded password with the given authInfo and authValue elements when
486   * using the authentication password syntax as defined in RFC 3112.  This
487   * should only be used if the {@link #supportsAuthPasswordSyntax} method
488   * returns {@code true}.
489   *
490   * @param  plaintextPassword  The plaintext password provided by the user.
491   * @param  authInfo           The authInfo component of the password encoded
492   *                            in the authentication password syntax.
493   * @param  authValue          The authValue component of the password encoded
494   *                            in the authentication password syntax.
495   * @param  userEntry          The complete entry for the user for whom the
496   *                            password is to be validated.  This will not be
497   *                            {@code null} for schemes in which
498   *                            {@link #requiresUserEntry} returns {@code true}.
499   *
500   * @return  {@code true} if the provided plaintext password could be used to
501   *          generate an encoded password with the given authInfo and authValue
502   *          portions, or {@code false} if not.
503   */
504  public boolean authPasswordMatches(final ByteString plaintextPassword,
505                                     final String authInfo,
506                                     final String authValue,
507                                     final Entry userEntry)
508  {
509    return false;
510  }
511
512
513
514  /**
515   * Obtains the plaintext password that was used to generate an encoded
516   * password with the given authInfo and authValue elements when using the
517   * authentication password syntax as described in RFC 3112.  This should only
518   * be used if both the {@link #supportsAuthPasswordSyntax} and
519   * {@link #isReversible} methods return {@code true}.
520   *
521   * @param  authInfo   The authInfo component of the password encoded in the
522   *                    authentication password syntax.
523   * @param  authValue  The authValue component of the password encoded in the
524   *                    authentication password syntax.
525   * @param  userEntry  The complete entry for the user for whom the password is
526   *                    to be validated.  This will not be {@code null} for
527   *                    schemes in which {@link #requiresUserEntry} returns
528   *                    {@code true}.  The entry should not be altered in any
529   *                    way by this password storage scheme.
530   *
531   * @return  The plaintext password that was used to generate the encoded
532   *          password.
533   *
534   * @throws  LDAPException  If this password storage scheme is not reversible,
535   *                         if this password storage scheme does not support
536   *                         the authentication password syntax, or if some
537   *                         other problem is encountered while attempting to
538   *                         determine the plaintext password.
539   */
540  public ByteString getAuthPasswordPlaintextValue(final String authInfo,
541                                                  final String authValue,
542                                                  final Entry userEntry)
543         throws LDAPException
544  {
545    throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
546         "This password storage scheme does not support the use of the " +
547         "authentication password syntax.");
548  }
549
550
551
552  /**
553   * {@inheritDoc}
554   */
555  public Map<List<String>,String> getExamplesArgumentSets()
556  {
557    return Collections.emptyMap();
558  }
559}