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-2014 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 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 }