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 * Indicates whether the provided password is encoded with the current 371 * settings for this password storage scheme. 372 * 373 * @param encodedPassword The encoded password for which to make the 374 * determination. This will not be {@code null}, and 375 * it will not include the scheme name in curly 376 * braces. 377 * @param userEntry The complete entry for the user with whom the 378 * password is associated. This will not be 379 * {@code null} for schemes in which 380 * {@link #requiresUserEntry()} returns {@code true}. 381 * The entry should not be altered in any way by this 382 * password storage scheme. 383 * @param explanations A list that may be updated with messages that may 384 * help explain the result that this method returns. 385 * This may include messages that explain why the 386 * provided password is not encoded with the current 387 * settings or why the determination could not be 388 * made. This will not be {@code null}, and it will 389 * be updatable. 390 * 391 * @return {@code true} if the provided password is encoded wit the current 392 * settings for this password storage scheme, {@code false} if it is 393 * not encoded with the current settings for this password storage 394 * scheme, or {@code null} if the determination cannot be made (e.g., 395 * because this method is not implemented for this password storage 396 * scheme, or because an error occurred while attempting to make 397 * the determination). 398 */ 399 public Boolean isPasswordEncodedWithCurrentSettings( 400 final ByteString encodedPassword, 401 final Entry userEntry, 402 final List<String> explanations) 403 { 404 explanations.add("The " + getStorageSchemeName() + " password storage " + 405 "scheme does not support determining whether a stored password is " + 406 "encoded with the current settings"); 407 return null; 408 } 409 410 411 412 /** 413 * Attempts to determine the plaintext password used to generate the provided 414 * encoded password. This method should only be called if the 415 * {@link #isReversible} method returns {@code true}. 416 * 417 * @param storedPassword The password for which to obtain the plaintext 418 * value. It should not include the scheme name in 419 * curly braces. 420 * @param userEntry The complete entry for the user for whom the 421 * password is to be validated. This will not be 422 * {@code null} for schemes in which 423 * {@link #requiresUserEntry} returns {@code true}. 424 * 425 * @return The plaintext password obtained from the given encoded password. 426 * 427 * @throws LDAPException If this password storage scheme is not reversible, 428 * or if the provided value could not be decoded to 429 * its plaintext representation. 430 */ 431 public abstract ByteString getPlaintextValue(final ByteString storedPassword, 432 final Entry userEntry) 433 throws LDAPException; 434 435 436 437 /** 438 * Indicates whether this password storage scheme provides the ability to 439 * encode passwords in the authentication password syntax as described in RFC 440 * 3112. 441 * 442 * @return {@code true} if this password storage scheme supports the 443 * authentication password syntax, or {@code false} if not. 444 */ 445 public boolean supportsAuthPasswordSyntax() 446 { 447 return false; 448 } 449 450 451 452 /** 453 * Retrieves the name that should be used to identify this password storage 454 * scheme when encoding passwords using the authentication password syntax as 455 * described in RFC 3112. This should only be used if the 456 * {@link #supportsAuthPasswordSyntax} method returns {@code true}. 457 * 458 * @return The name that should be used to identify this password storage 459 * scheme when encoding passwords using the authentication password 460 * syntax. 461 */ 462 public String getAuthPasswordSchemeName() 463 { 464 return getStorageSchemeName(); 465 } 466 467 468 469 /** 470 * Encodes the provided plaintext password using the authentication password 471 * syntax as defined in RFC 3112. This should only be used if the 472 * {@link #supportsAuthPasswordSyntax} method returns {@code true}. 473 * 474 * @param plaintext The plaintext password to be encoded. It must not 475 * be {@code null}. Note that there is no guarantee 476 * that password validators have yet been invoked for 477 * this password, so this password storage scheme 478 * implementation should not make any assumptions about 479 * the format of the plaintext password or whether it 480 * will actually be allowed for use in the entry. 481 * @param userEntry The complete entry for the user for whom the 482 * password is to be encoded. This will not be 483 * {@code null} for schemes in which 484 * {@link #requiresUserEntry} returns {@code true}. 485 * @param modifications The set of modifications to be applied to the user 486 * entry. This will generally be non-{@code null} only 487 * for operations that use a modify to change the user 488 * password. If a modification list is provided, then 489 * it will contain the complete set of modifications 490 * for the operation, some of which may have no 491 * impact on password encoding. 492 * @param deterministic Indicates whether the password encoding should be 493 * deterministic. If this is {@code true}, then the 494 * scheme should attempt to generate a consistent 495 * encoding for the password (e.g., by determining the 496 * salt from a normalized representation of the user's 497 * DN). This will be used during LDIF import 498 * processing (and potentially at other times) to help 499 * ensure that if the LDIF file is imported into 500 * multiple servers, any clear-text password 501 * encountered in the LDIF file will have the same 502 * encoded representation on all servers. Some 503 * password storage schemes may choose to ignore this 504 * property if it does not apply or is not possible to 505 * achieve. 506 * 507 * @return The encoded representation of the provided password. 508 * 509 * @throws LDAPException If a problem occurs while encoding the provided 510 * password, or if this password storage scheme does 511 * not support the authentication password syntax. 512 */ 513 public ByteString encodeAuthPassword(final ByteString plaintext, 514 final Entry userEntry, 515 final List<Modification> modifications, 516 final boolean deterministic) 517 throws LDAPException 518 { 519 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 520 "This password storage scheme does not support the use of the " + 521 "authentication password syntax."); 522 } 523 524 525 526 /** 527 * Indicates whether the provided plaintext password may be used to generate 528 * an encoded password with the given authInfo and authValue elements when 529 * using the authentication password syntax as defined in RFC 3112. This 530 * should only be used if the {@link #supportsAuthPasswordSyntax} method 531 * returns {@code true}. 532 * 533 * @param plaintextPassword The plaintext password provided by the user. 534 * @param authInfo The authInfo component of the password encoded 535 * in the authentication password syntax. 536 * @param authValue The authValue component of the password encoded 537 * in the authentication password syntax. 538 * @param userEntry The complete entry for the user for whom the 539 * password is to be validated. This will not be 540 * {@code null} for schemes in which 541 * {@link #requiresUserEntry} returns {@code true}. 542 * 543 * @return {@code true} if the provided plaintext password could be used to 544 * generate an encoded password with the given authInfo and authValue 545 * portions, or {@code false} if not. 546 */ 547 public boolean authPasswordMatches(final ByteString plaintextPassword, 548 final String authInfo, 549 final String authValue, 550 final Entry userEntry) 551 { 552 return false; 553 } 554 555 556 557 /** 558 * Indicates whether the provided password is encoded with the current 559 * settings for this password storage scheme using the authentication password 560 * syntax. 561 * 562 * @param authInfo The authInfo component of the password encoded in the 563 * authentication password syntax. 564 * @param authValue The authValue component of the password encoded in 565 * the authentication password syntax. 566 * @param userEntry The complete entry for the user with whom the 567 * password is associated. This will not be 568 * {@code null} for wchemes in which 569 * {@link #requiresUserEntry()} returns {@code true}. 570 * The entry should not be altered in any way by this 571 * password storage scheme. 572 * @param explanations A list that may be updated with messages that may 573 * help explain the result that this method returns. 574 * This may include messages that explain why the 575 * provided password is not encoded with the current 576 * settings or why the determination could not be made. 577 * This will not be {@code null}, and it will be 578 * updatable. 579 * 580 * @return {@code true} if the provided password is encoded wit the current 581 * settings for this password storage scheme, {@code false} if it is 582 * not encoded with the current settings for this password storage 583 * scheme, or {@code null} if the determination cannot be made (e.g., 584 * because this method is not implemented for this password storage 585 * scheme, or because an error occurred while attempting to make 586 * the determination). 587 */ 588 public Boolean isAuthPasswordEncodedWithCurrentSettings( 589 final String authInfo, final String authValue, 590 final Entry userEntry, 591 final List<String> explanations) 592 { 593 explanations.add("The " + getAuthPasswordSchemeName() + " password " + 594 "storage scheme does not support determining whether a stored " + 595 "password is encoded with the current settings"); 596 return null; 597 } 598 599 600 601 /** 602 * Obtains the plaintext password that was used to generate an encoded 603 * password with the given authInfo and authValue elements when using the 604 * authentication password syntax as described in RFC 3112. This should only 605 * be used if both the {@link #supportsAuthPasswordSyntax} and 606 * {@link #isReversible} methods return {@code true}. 607 * 608 * @param authInfo The authInfo component of the password encoded in the 609 * authentication password syntax. 610 * @param authValue The authValue component of the password encoded in the 611 * authentication password syntax. 612 * @param userEntry The complete entry for the user for whom the password is 613 * to be validated. This will not be {@code null} for 614 * schemes in which {@link #requiresUserEntry} returns 615 * {@code true}. The entry should not be altered in any 616 * way by this password storage scheme. 617 * 618 * @return The plaintext password that was used to generate the encoded 619 * password. 620 * 621 * @throws LDAPException If this password storage scheme is not reversible, 622 * if this password storage scheme does not support 623 * the authentication password syntax, or if some 624 * other problem is encountered while attempting to 625 * determine the plaintext password. 626 */ 627 public ByteString getAuthPasswordPlaintextValue(final String authInfo, 628 final String authValue, 629 final Entry userEntry) 630 throws LDAPException 631 { 632 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 633 "This password storage scheme does not support the use of the " + 634 "authentication password syntax."); 635 } 636 637 638 639 /** 640 * {@inheritDoc} 641 */ 642 public Map<List<String>,String> getExamplesArgumentSets() 643 { 644 return Collections.emptyMap(); 645 } 646}