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 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
035 import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
036 import com.unboundid.directory.sdk.common.internal.Reconfigurable;
037 import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
038 import com.unboundid.directory.sdk.common.types.Entry;
039 import com.unboundid.directory.sdk.ds.config.PasswordStorageSchemeConfig;
040 import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
041 import com.unboundid.directory.sdk.ds.types.DirectoryServerContext;
042 import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
043 import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
044 import com.unboundid.ldap.sdk.LDAPException;
045 import com.unboundid.ldap.sdk.Modification;
046 import com.unboundid.ldap.sdk.ResultCode;
047 import com.unboundid.util.ByteString;
048 import com.unboundid.util.Extensible;
049 import com.unboundid.util.ThreadSafety;
050 import com.unboundid.util.ThreadSafetyLevel;
051 import com.unboundid.util.args.ArgumentException;
052 import 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)
110 public 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 version of the password. This will
297 * not be {@code null}.
298 * @param userEntry The complete entry for the user for whom the
299 * password is to be encoded. This will not be
300 * {@code null} for schemes in which
301 * {@link #requiresUserEntry} returns {@code true}.
302 * @param modifications The set of modifications to be applied to the user
303 * entry. This will generally be non-{@code null} only
304 * for operations that use a modify to change the user
305 * password. If a modification list is provided, then
306 * it will contain the complete set of modifications
307 * for the operation, some of which may have no
308 * impact on password encoding.
309 * @param deterministic Indicates whether the password encoding should be
310 * deterministic. If this is {@code true}, then the
311 * scheme should attempt to generate a consistent
312 * encoding for the password (e.g., by determining the
313 * salt from a normalized representation of the user's
314 * DN). This will be used during LDIF import
315 * processing (and potentially at other times) to help
316 * ensure that if the LDIF file is imported into
317 * multiple servers, any clear-text password
318 * encountered in the LDIF file will have the same
319 * encoded representation on all servers. Some
320 * password storage schemes may choose to ignore this
321 * property if it does not apply or is not possible to
322 * achieve.
323 * @param includeScheme Indicates whether to include the name of the scheme
324 * in curly braces before the encoded password.
325 *
326 * @return The password that has been encoded using this storage
327 * scheme.
328 *
329 * @throws LDAPException If a problem occurs while attempting to encode the
330 * password.
331 */
332 public abstract ByteString encodePassword(final ByteString plaintext,
333 final Entry userEntry,
334 final List<Modification> modifications,
335 final boolean deterministic,
336 final boolean includeScheme)
337 throws LDAPException;
338
339
340
341 /**
342 * Indicates whether the provided plaintext password could have been used to
343 * generate the given encoded password.
344 *
345 * @param plaintextPassword The plaintext password provided by the user as
346 * part of a simple bind attempt.
347 * @param storedPassword The stored password to compare against the
348 * provided plaintext password. It will not
349 * include the scheme name in curly braces.
350 * @param userEntry The complete entry for the user for whom the
351 * password is to be validated. This will not be
352 * {@code null} for schemes in which
353 * {@link #requiresUserEntry} returns {@code true}.
354 *
355 * @return {@code true} if the provided clear-text password could have been
356 * used to generate the encoded password, or {@code false} if not.
357 */
358 public abstract boolean passwordMatches(final ByteString plaintextPassword,
359 final ByteString storedPassword,
360 final Entry userEntry);
361
362
363
364 /**
365 * Attempts to determine the plaintext password used to generate the provided
366 * encoded password. This method should only be called if the
367 * {@link #isReversible} method returns {@code true}.
368 *
369 * @param storedPassword The password for which to obtain the plaintext
370 * value. It should not include the scheme name in
371 * curly braces.
372 * @param userEntry The complete entry for the user for whom the
373 * password is to be validated. This will not be
374 * {@code null} for schemes in which
375 * {@link #requiresUserEntry} returns {@code true}.
376 *
377 * @return The plaintext password obtained from the given encoded password.
378 *
379 * @throws LDAPException If this password storage scheme is not reversible,
380 * or if the provided value could not be decoded to
381 * its plaintext representation.
382 */
383 public abstract ByteString getPlaintextValue(final ByteString storedPassword,
384 final Entry userEntry)
385 throws LDAPException;
386
387
388
389 /**
390 * Indicates whether this password storage scheme provides the ability to
391 * encode passwords in the authentication password syntax as described in RFC
392 * 3112.
393 *
394 * @return {@code true} if this password storage scheme supports the
395 * authentication password syntax, or {@code false} if not.
396 */
397 public boolean supportsAuthPasswordSyntax()
398 {
399 return false;
400 }
401
402
403
404 /**
405 * Retrieves the name that should be used to identify this password storage
406 * scheme when encoding passwords using the authentication password syntax as
407 * described in RFC 3112. This should only be used if the
408 * {@link #supportsAuthPasswordSyntax} method returns {@code true}.
409 *
410 * @return The name that should be used to identify this password storage
411 * scheme when encoding passwords using the authentication password
412 * syntax.
413 */
414 public String getAuthPasswordSchemeName()
415 {
416 return getStorageSchemeName();
417 }
418
419
420
421 /**
422 * Encodes the provided plaintext password using the authentication password
423 * syntax as defined in RFC 3112. This should only be used if the
424 * {@link #supportsAuthPasswordSyntax} method returns {@code true}.
425 *
426 * @param plaintext The plaintext version of the password. This will
427 * not be {@code null}.
428 * @param userEntry The complete entry for the user for whom the
429 * password is to be encoded. This will not be
430 * {@code null} for schemes in which
431 * {@link #requiresUserEntry} returns {@code true}.
432 * @param modifications The set of modifications to be applied to the user
433 * entry. This will generally be non-{@code null} only
434 * for operations that use a modify to change the user
435 * password. If a modification list is provided, then
436 * it will contain the complete set of modifications
437 * for the operation, some of which may have no
438 * impact on password encoding.
439 * @param deterministic Indicates whether the password encoding should be
440 * deterministic. If this is {@code true}, then the
441 * scheme should attempt to generate a consistent
442 * encoding for the password (e.g., by determining the
443 * salt from a normalized representation of the user's
444 * DN). This will be used during LDIF import
445 * processing (and potentially at other times) to help
446 * ensure that if the LDIF file is imported into
447 * multiple servers, any clear-text password
448 * encountered in the LDIF file will have the same
449 * encoded representation on all servers. Some
450 * password storage schemes may choose to ignore this
451 * property if it does not apply or is not possible to
452 * achieve.
453 *
454 * @return The encoded representation of the provided password.
455 *
456 * @throws LDAPException If a problem occurs while encoding the provided
457 * password, or if this password storage scheme does
458 * not support the authentication password syntax.
459 */
460 public ByteString encodeAuthPassword(final ByteString plaintext,
461 final Entry userEntry,
462 final List<Modification> modifications,
463 final boolean deterministic)
464 throws LDAPException
465 {
466 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
467 "This password storage scheme does not support the use of the " +
468 "authentication password syntax.");
469 }
470
471
472
473 /**
474 * Indicates whether the provided plaintext password may be used to generate
475 * an encoded password with the given authInfo and authValue elements when
476 * using the authentication password syntax as defined in RFC 3112. This
477 * should only be used if the {@link #supportsAuthPasswordSyntax} method
478 * returns {@code true}.
479 *
480 * @param plaintextPassword The plaintext password provided by the user.
481 * @param authInfo The authInfo component of the password encoded
482 * in the authentication password syntax.
483 * @param authValue The authValue component of the password encoded
484 * in the authentication password syntax.
485 * @param userEntry The complete entry for the user for whom the
486 * password is to be validated. This will not be
487 * {@code null} for schemes in which
488 * {@link #requiresUserEntry} returns {@code true}.
489 *
490 * @return {@code true} if the provided plaintext password could be used to
491 * generate an encoded password with the given authInfo and authValue
492 * portions, or {@code false} if not.
493 */
494 public boolean authPasswordMatches(final ByteString plaintextPassword,
495 final String authInfo,
496 final String authValue,
497 final Entry userEntry)
498 {
499 return false;
500 }
501
502
503
504 /**
505 * Obtains the plaintext password that was used to generate an encoded
506 * password with the given authInfo and authValue elements when using the
507 * authentication password syntax as described in RFC 3112. This should only
508 * be used if both the {@link #supportsAuthPasswordSyntax} and
509 * {@link #isReversible} methods return {@code true}.
510 *
511 * @param authInfo The authInfo component of the password encoded in the
512 * authentication password syntax.
513 * @param authValue The authValue component of the password encoded in the
514 * authentication password syntax.
515 * @param userEntry The complete entry for the user for whom the password is
516 * to be validated. This will not be {@code null} for
517 * schemes in which {@link #requiresUserEntry} returns
518 * {@code true}. The entry should not be altered in any
519 * way by this password storage scheme.
520 *
521 * @return The plaintext password that was used to generate the encoded
522 * password.
523 *
524 * @throws LDAPException If this password storage scheme is not reversible,
525 * if this password storage scheme does not support
526 * the authentication password syntax, or if some
527 * other problem is encountered while attempting to
528 * determine the plaintext password.
529 */
530 public ByteString getAuthPasswordPlaintextValue(final String authInfo,
531 final String authValue,
532 final Entry userEntry)
533 throws LDAPException
534 {
535 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
536 "This password storage scheme does not support the use of the " +
537 "authentication password syntax.");
538 }
539
540
541
542 /**
543 * {@inheritDoc}
544 */
545 public Map<List<String>,String> getExamplesArgumentSets()
546 {
547 return Collections.emptyMap();
548 }
549 }