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}