001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2010 Sun Microsystems, Inc. 015 * Portions Copyright 2011-2016 ForgeRock AS. 016 */ 017package org.opends.server.core; 018 019import static org.opends.messages.ConfigMessages.*; 020import static org.opends.messages.CoreMessages.*; 021import static org.opends.server.schema.SchemaConstants.*; 022 023import java.util.Collection; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028import java.util.SortedSet; 029import java.util.concurrent.atomic.AtomicBoolean; 030 031import org.forgerock.i18n.LocalizableMessage; 032import org.forgerock.i18n.LocalizableMessageBuilder; 033import org.forgerock.i18n.slf4j.LocalizedLogger; 034import org.forgerock.opendj.config.server.ConfigException; 035import org.forgerock.opendj.ldap.ByteString; 036import org.forgerock.opendj.ldap.ResultCode; 037import org.forgerock.opendj.ldap.schema.AttributeType; 038import org.opends.server.admin.std.meta.PasswordPolicyCfgDefn.StateUpdateFailurePolicy; 039import org.opends.server.admin.std.server.PasswordValidatorCfg; 040import org.opends.server.api.AccountStatusNotificationHandler; 041import org.opends.server.api.PasswordGenerator; 042import org.opends.server.api.PasswordStorageScheme; 043import org.opends.server.api.PasswordValidator; 044import org.opends.server.types.Attribute; 045import org.forgerock.opendj.ldap.DN; 046import org.opends.server.types.DirectoryException; 047import org.opends.server.types.Entry; 048import org.opends.server.types.InitializationException; 049import org.opends.server.types.ObjectClass; 050import org.opends.server.types.Operation; 051import org.opends.server.types.SubEntry; 052 053/** 054 * This class represents subentry password policy based on Password Policy for 055 * LDAP Directories Internet-Draft. In order to represent subentry password 056 * policies as OpenDJ password policies it performs a mapping of Draft defined 057 * attributes to OpenDJ implementation specific attributes. Any missing 058 * attributes are inherited from server default password policy. This class is 059 * also responsible for any Draft attributes validation ie making sure that 060 * provided values are acceptable and within the predefined range. 061 */ 062public final class SubentryPasswordPolicy extends PasswordPolicy 063{ 064 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 065 066 // Password Policy Subentry draft attributes. 067 private static final String PWD_OC_POLICY = "pwdpolicy"; 068 private static final String PWD_ATTR_ATTRIBUTE = "pwdattribute"; 069 private static final String PWD_ATTR_MINAGE = "pwdminage"; 070 private static final String PWD_ATTR_MAXAGE = "pwdmaxage"; 071 private static final String PWD_ATTR_INHISTORY = "pwdinhistory"; 072 private static final String PWD_ATTR_CHECKQUALITY = "pwdcheckquality"; 073 private static final String PWD_ATTR_MINLENGTH = "pwdminlength"; 074 private static final String PWD_ATTR_EXPIREWARNING = "pwdexpirewarning"; 075 private static final String PWD_ATTR_GRACEAUTHNLIMIT = "pwdgraceauthnlimit"; 076 private static final String PWD_ATTR_LOCKOUT = "pwdlockout"; 077 private static final String PWD_ATTR_LOCKOUTDURATION = "pwdlockoutduration"; 078 private static final String PWD_ATTR_MAXFAILURE = "pwdmaxfailure"; 079 private static final String PWD_ATTR_MUSTCHANGE = "pwdmustchange"; 080 private static final String PWD_ATTR_ALLOWUSERCHANGE = "pwdallowuserchange"; 081 private static final String PWD_ATTR_SAFEMODIFY = "pwdsafemodify"; 082 private static final String PWD_ATTR_FAILURECOUNTINTERVAL = 083 "pwdfailurecountinterval"; 084 private static final String PWD_ATTR_VALIDATOR = "ds-cfg-password-validator"; 085 private static final String PWD_OC_VALIDATORPOLICY = "pwdvalidatorpolicy"; 086 087 /** Password Policy Subentry DN. */ 088 private final DN passwordPolicySubentryDN; 089 /** The value of the "allow-user-password-changes" property. */ 090 private final Boolean pAllowUserPasswordChanges; 091 /** The value of the "force-change-on-reset" property. */ 092 private final Boolean pForceChangeOnReset; 093 /** The value of the "grace-login-count" property. */ 094 private final Integer pGraceLoginCount; 095 /** The value of the "lockout-duration" property. */ 096 private final Long pLockoutDuration; 097 /** The value of the "lockout-failure-count" property. */ 098 private final Integer pLockoutFailureCount; 099 /** The value of the "lockout-failure-expiration-interval" property. */ 100 private final Long pLockoutFailureExpirationInterval; 101 /** The value of the "max-password-age" property. */ 102 private final Long pMaxPasswordAge; 103 /** The value of the "min-password-age" property. */ 104 private final Long pMinPasswordAge; 105 /** The value of the "password-attribute" property. */ 106 private final AttributeType pPasswordAttribute; 107 /** The value of the "password-change-requires-current-password" property. */ 108 private final Boolean pPasswordChangeRequiresCurrentPassword; 109 /** The value of the "password-expiration-warning-interval" property. */ 110 private final Long pPasswordExpirationWarningInterval; 111 /** The value of the "password-history-count" property. */ 112 private final Integer pPasswordHistoryCount; 113 /** Indicates if the password attribute uses auth password syntax. */ 114 private final Boolean pAuthPasswordSyntax; 115 /** The set of password validators if any. */ 116 private final Set<DN> pValidatorNames = new HashSet<>(); 117 /** Used when logging errors due to invalid validator reference. */ 118 private AtomicBoolean isAlreadyLogged = new AtomicBoolean(); 119 120 /** 121 * Returns the global default password policy which will be used for deriving 122 * the default properties of sub-entries. 123 */ 124 private PasswordPolicy getDefaultPasswordPolicy() 125 { 126 return DirectoryServer.getDefaultPasswordPolicy(); 127 } 128 129 /** 130 * Creates subentry password policy object from the subentry, parsing and 131 * evaluating subentry password policy attributes. 132 * 133 * @param subentry 134 * password policy subentry. 135 * @throws DirectoryException 136 * If a problem occurs while creating subentry password policy 137 * instance from given subentry. 138 */ 139 public SubentryPasswordPolicy(SubEntry subentry) throws DirectoryException 140 { 141 // Determine if this is a password policy subentry. 142 ObjectClass pwdPolicyOC = DirectoryServer.getObjectClass(PWD_OC_POLICY); 143 Entry entry = subentry.getEntry(); 144 Map<ObjectClass, String> objectClasses = entry.getObjectClasses(); 145 if (pwdPolicyOC == null) 146 { 147 // This should not happen -- The server doesn't 148 // have a pwdPolicy objectclass defined. 149 if (logger.isTraceEnabled()) 150 { 151 logger.trace("No %s objectclass is defined in the server schema.", 152 PWD_OC_POLICY); 153 } 154 for (String ocName : objectClasses.values()) 155 { 156 if (PWD_OC_POLICY.equalsIgnoreCase(ocName)) 157 { 158 break; 159 } 160 } 161 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 162 ERR_PWPOLICY_NO_PWDPOLICY_OC.get(subentry.getDN())); 163 } 164 else if (!objectClasses.containsKey(pwdPolicyOC)) 165 { 166 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 167 ERR_PWPOLICY_NO_PWDPOLICY_OC.get(subentry.getDN())); 168 } 169 170 // Subentry DN for this password policy. 171 this.passwordPolicySubentryDN = subentry.getDN(); 172 173 // Get known Password Policy draft attributes from the entry. 174 // If any given attribute is missing or empty set its value 175 // from default Password Policy configuration. 176 String value = getAttrValue(entry, PWD_ATTR_ATTRIBUTE); 177 if (value != null && value.length() > 0) 178 { 179 this.pPasswordAttribute = DirectoryServer.getAttributeType(value); 180 if (this.pPasswordAttribute.isPlaceHolder()) 181 { 182 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 183 ERR_PWPOLICY_UNDEFINED_PASSWORD_ATTRIBUTE.get(this.passwordPolicySubentryDN, value)); 184 } 185 186 // Check the syntax. 187 final String syntaxOID = pPasswordAttribute.getSyntax().getOID(); 188 if (SYNTAX_AUTH_PASSWORD_OID.equals(syntaxOID)) 189 { 190 pAuthPasswordSyntax = true; 191 } 192 else if (SYNTAX_USER_PASSWORD_OID.equals(syntaxOID)) 193 { 194 pAuthPasswordSyntax = false; 195 } 196 else 197 { 198 String syntax = pPasswordAttribute.getSyntax().getName(); 199 if (syntax == null || syntax.length() == 0) 200 { 201 syntax = syntaxOID; 202 } 203 204 LocalizableMessage message = ERR_PWPOLICY_INVALID_PASSWORD_ATTRIBUTE_SYNTAX.get( 205 passwordPolicySubentryDN, pPasswordAttribute.getNameOrOID(), syntax); 206 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 207 } 208 } 209 else 210 { 211 this.pPasswordAttribute = null; 212 this.pAuthPasswordSyntax = null; 213 } 214 215 this.pMinPasswordAge = asLong(entry, PWD_ATTR_MINAGE); 216 this.pMaxPasswordAge = asLong(entry, PWD_ATTR_MAXAGE); 217 this.pPasswordHistoryCount = 218 asInteger(entry, PWD_ATTR_INHISTORY, Integer.MAX_VALUE); 219 220 // This one is managed via the password validator 221 // so only check if its value is acceptable. 222 asInteger(entry, PWD_ATTR_CHECKQUALITY, 2); 223 224 // This one is managed via the password validator 225 // so only check if its value is acceptable. 226 asInteger(entry, PWD_ATTR_MINLENGTH, Integer.MAX_VALUE); 227 228 // This one depends on lockout failure count value 229 // so only check if its value is acceptable. 230 asBoolean(entry, PWD_ATTR_LOCKOUT); 231 232 this.pPasswordExpirationWarningInterval = 233 asLong(entry, PWD_ATTR_EXPIREWARNING); 234 this.pGraceLoginCount = 235 asInteger(entry, PWD_ATTR_GRACEAUTHNLIMIT, Integer.MAX_VALUE); 236 this.pLockoutDuration = asLong(entry, PWD_ATTR_LOCKOUTDURATION); 237 this.pLockoutFailureCount = 238 asInteger(entry, PWD_ATTR_MAXFAILURE, Integer.MAX_VALUE); 239 this.pForceChangeOnReset = asBoolean(entry, PWD_ATTR_MUSTCHANGE); 240 this.pAllowUserPasswordChanges = asBoolean(entry, PWD_ATTR_ALLOWUSERCHANGE); 241 this.pPasswordChangeRequiresCurrentPassword = 242 asBoolean(entry, PWD_ATTR_SAFEMODIFY); 243 this.pLockoutFailureExpirationInterval = 244 asLong(entry, PWD_ATTR_FAILURECOUNTINTERVAL); 245 246 // Now check for the pwdValidatorPolicy OC and its attribute. 247 // Determine if this is a password validator policy object class. 248 ObjectClass pwdValidatorPolicyOC = 249 DirectoryServer.getObjectClass(PWD_OC_VALIDATORPOLICY); 250 if (pwdValidatorPolicyOC != null && 251 objectClasses.containsKey(pwdValidatorPolicyOC)) 252 { 253 AttributeType pwdAttrType = 254 DirectoryServer.getAttributeType(PWD_ATTR_VALIDATOR); 255 for (Attribute attr : entry.getAttribute(pwdAttrType)) 256 { 257 for (ByteString val : attr) 258 { 259 DN validatorDN = DN.valueOf(val); 260 if (DirectoryServer.getPasswordValidator(validatorDN) == null) 261 { 262 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 263 ERR_PWPOLICY_UNKNOWN_VALIDATOR.get(this.passwordPolicySubentryDN, validatorDN, PWD_ATTR_VALIDATOR)); 264 } 265 pValidatorNames.add(validatorDN); 266 } 267 } 268 } 269 } 270 271 private Boolean asBoolean(Entry entry, String attrName) 272 throws DirectoryException 273 { 274 final String value = getAttrValue(entry, attrName); 275 if (value != null && value.length() > 0) 276 { 277 if (value.equalsIgnoreCase(Boolean.TRUE.toString()) 278 || value.equalsIgnoreCase(Boolean.FALSE.toString())) 279 { 280 return Boolean.valueOf(value); 281 } 282 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 283 ERR_CONFIG_ATTR_INVALID_BOOLEAN_VALUE.get(attrName, value)); 284 } 285 return null; 286 } 287 288 private Integer asInteger(Entry entry, String attrName, int upperBound) 289 throws DirectoryException 290 { 291 final String value = getAttrValue(entry, attrName); 292 if (value != null && value.length() > 0) 293 { 294 try 295 { 296 final Integer result = Integer.valueOf(value); 297 checkIntegerAttr(attrName, result, 0, upperBound); 298 return result; 299 } 300 catch (NumberFormatException ne) 301 { 302 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 303 ERR_CONFIG_ATTR_INVALID_INT_VALUE.get(attrName, value, 304 ne.getLocalizedMessage())); 305 } 306 } 307 return null; 308 } 309 310 private Long asLong(Entry entry, String attrName) throws DirectoryException 311 { 312 final String value = getAttrValue(entry, attrName); 313 if (value != null && value.length() > 0) 314 { 315 try 316 { 317 final Long result = Long.valueOf(value); 318 checkIntegerAttr(attrName, result, 0, Integer.MAX_VALUE); 319 return result; 320 } 321 catch (NumberFormatException ne) 322 { 323 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 324 ERR_CONFIG_ATTR_INVALID_INT_VALUE.get(attrName, value, 325 ne.getLocalizedMessage())); 326 } 327 } 328 return null; 329 } 330 331 332 333 /** 334 * Helper method to validate integer values. 335 * 336 * @param attrName 337 * integer attribute name. 338 * @param attrValue 339 * integer value to validate. 340 * @param lowerBound 341 * lowest acceptable value. 342 * @param upperBound 343 * highest acceptable value. 344 * @throws DirectoryException 345 * if the value is out of bounds. 346 */ 347 private void checkIntegerAttr(String attrName, long attrValue, 348 long lowerBound, long upperBound) throws DirectoryException 349 { 350 if (attrValue < lowerBound) 351 { 352 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 353 ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get(attrName, attrValue, 354 lowerBound)); 355 } 356 if (attrValue > upperBound) 357 { 358 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 359 ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get(attrName, attrValue, 360 upperBound)); 361 } 362 } 363 364 365 366 /** 367 * Helper method to retrieve an attribute value from given entry. 368 * 369 * @param entry 370 * the entry to retrieve an attribute value from. 371 * @param pwdAttrName 372 * attribute name to retrieve the value for. 373 * @return <CODE>String</CODE> or <CODE>null</CODE>. 374 */ 375 private String getAttrValue(Entry entry, String pwdAttrName) 376 { 377 AttributeType pwdAttrType = DirectoryServer.getAttributeType(pwdAttrName); 378 for (Attribute attr : entry.getAttribute(pwdAttrType)) 379 { 380 for (ByteString value : attr) 381 { 382 return value.toString(); 383 } 384 } 385 return null; 386 } 387 388 @Override 389 public boolean isAllowExpiredPasswordChanges() 390 { 391 return getDefaultPasswordPolicy().isAllowExpiredPasswordChanges(); 392 } 393 394 @Override 395 public boolean isAllowMultiplePasswordValues() 396 { 397 return getDefaultPasswordPolicy().isAllowMultiplePasswordValues(); 398 } 399 400 @Override 401 public boolean isAllowPreEncodedPasswords() 402 { 403 return getDefaultPasswordPolicy().isAllowPreEncodedPasswords(); 404 } 405 406 @Override 407 public boolean isAllowUserPasswordChanges() 408 { 409 return pAllowUserPasswordChanges != null ? pAllowUserPasswordChanges 410 : getDefaultPasswordPolicy().isAllowUserPasswordChanges(); 411 } 412 413 /** {@inheritDoc} */ 414 @Override 415 public boolean isExpirePasswordsWithoutWarning() 416 { 417 return getDefaultPasswordPolicy().isExpirePasswordsWithoutWarning(); 418 } 419 420 /** {@inheritDoc} */ 421 @Override 422 public boolean isForceChangeOnAdd() 423 { 424 // Don't use pwdMustChange since the password provided when the entry was 425 // added may have been provided by the user. See OPENDJ-341. 426 return getDefaultPasswordPolicy().isForceChangeOnAdd(); 427 } 428 429 /** {@inheritDoc} */ 430 @Override 431 public boolean isForceChangeOnReset() 432 { 433 return pForceChangeOnReset != null ? pForceChangeOnReset 434 : getDefaultPasswordPolicy().isForceChangeOnReset(); 435 } 436 437 /** {@inheritDoc} */ 438 @Override 439 public int getGraceLoginCount() 440 { 441 return pGraceLoginCount != null ? pGraceLoginCount 442 : getDefaultPasswordPolicy().getGraceLoginCount(); 443 } 444 445 /** {@inheritDoc} */ 446 @Override 447 public long getIdleLockoutInterval() 448 { 449 return getDefaultPasswordPolicy().getIdleLockoutInterval(); 450 } 451 452 /** {@inheritDoc} */ 453 @Override 454 public AttributeType getLastLoginTimeAttribute() 455 { 456 return getDefaultPasswordPolicy().getLastLoginTimeAttribute(); 457 } 458 459 /** {@inheritDoc} */ 460 @Override 461 public String getLastLoginTimeFormat() 462 { 463 return getDefaultPasswordPolicy().getLastLoginTimeFormat(); 464 } 465 466 /** {@inheritDoc} */ 467 @Override 468 public long getLockoutDuration() 469 { 470 return pLockoutDuration != null ? pLockoutDuration 471 : getDefaultPasswordPolicy().getLockoutDuration(); 472 } 473 474 /** {@inheritDoc} */ 475 @Override 476 public int getLockoutFailureCount() 477 { 478 return pLockoutFailureCount != null ? pLockoutFailureCount 479 : getDefaultPasswordPolicy().getLockoutFailureCount(); 480 } 481 482 /** {@inheritDoc} */ 483 @Override 484 public long getLockoutFailureExpirationInterval() 485 { 486 return pLockoutFailureExpirationInterval != null ? 487 pLockoutFailureExpirationInterval 488 : getDefaultPasswordPolicy().getLockoutFailureExpirationInterval(); 489 } 490 491 /** {@inheritDoc} */ 492 @Override 493 public long getMaxPasswordAge() 494 { 495 return pMaxPasswordAge != null ? pMaxPasswordAge 496 : getDefaultPasswordPolicy().getMaxPasswordAge(); 497 } 498 499 /** {@inheritDoc} */ 500 @Override 501 public long getMaxPasswordResetAge() 502 { 503 return getDefaultPasswordPolicy().getMaxPasswordResetAge(); 504 } 505 506 /** {@inheritDoc} */ 507 @Override 508 public long getMinPasswordAge() 509 { 510 return pMinPasswordAge != null ? pMinPasswordAge 511 : getDefaultPasswordPolicy().getMinPasswordAge(); 512 } 513 514 /** {@inheritDoc} */ 515 @Override 516 public AttributeType getPasswordAttribute() 517 { 518 return pPasswordAttribute != null ? pPasswordAttribute 519 : getDefaultPasswordPolicy().getPasswordAttribute(); 520 } 521 522 /** {@inheritDoc} */ 523 @Override 524 public boolean isPasswordChangeRequiresCurrentPassword() 525 { 526 return pPasswordChangeRequiresCurrentPassword != null ? 527 pPasswordChangeRequiresCurrentPassword 528 : getDefaultPasswordPolicy().isPasswordChangeRequiresCurrentPassword(); 529 } 530 531 /** {@inheritDoc} */ 532 @Override 533 public long getPasswordExpirationWarningInterval() 534 { 535 return pPasswordExpirationWarningInterval != null ? 536 pPasswordExpirationWarningInterval 537 : getDefaultPasswordPolicy().getPasswordExpirationWarningInterval(); 538 } 539 540 /** {@inheritDoc} */ 541 @Override 542 public int getPasswordHistoryCount() 543 { 544 return pPasswordHistoryCount != null ? pPasswordHistoryCount 545 : getDefaultPasswordPolicy().getPasswordHistoryCount(); 546 } 547 548 /** {@inheritDoc} */ 549 @Override 550 public long getPasswordHistoryDuration() 551 { 552 return getDefaultPasswordPolicy().getPasswordHistoryDuration(); 553 } 554 555 /** {@inheritDoc} */ 556 @Override 557 public SortedSet<String> getPreviousLastLoginTimeFormats() 558 { 559 return getDefaultPasswordPolicy().getPreviousLastLoginTimeFormats(); 560 } 561 562 /** {@inheritDoc} */ 563 @Override 564 public long getRequireChangeByTime() 565 { 566 return getDefaultPasswordPolicy().getRequireChangeByTime(); 567 } 568 569 /** {@inheritDoc} */ 570 @Override 571 public boolean isRequireSecureAuthentication() 572 { 573 return getDefaultPasswordPolicy().isRequireSecureAuthentication(); 574 } 575 576 /** {@inheritDoc} */ 577 @Override 578 public boolean isRequireSecurePasswordChanges() 579 { 580 return getDefaultPasswordPolicy().isRequireSecurePasswordChanges(); 581 } 582 583 /** {@inheritDoc} */ 584 @Override 585 public boolean isSkipValidationForAdministrators() 586 { 587 return getDefaultPasswordPolicy().isSkipValidationForAdministrators(); 588 } 589 590 /** {@inheritDoc} */ 591 @Override 592 public StateUpdateFailurePolicy getStateUpdateFailurePolicy() 593 { 594 return getDefaultPasswordPolicy().getStateUpdateFailurePolicy(); 595 } 596 597 /** {@inheritDoc} */ 598 @Override 599 public boolean isAuthPasswordSyntax() 600 { 601 return pAuthPasswordSyntax != null ? pAuthPasswordSyntax 602 : getDefaultPasswordPolicy().isAuthPasswordSyntax(); 603 } 604 605 /** {@inheritDoc} */ 606 @Override 607 public List<PasswordStorageScheme<?>> getDefaultPasswordStorageSchemes() 608 { 609 return getDefaultPasswordPolicy().getDefaultPasswordStorageSchemes(); 610 } 611 612 /** {@inheritDoc} */ 613 @Override 614 public Set<String> getDeprecatedPasswordStorageSchemes() 615 { 616 return getDefaultPasswordPolicy().getDeprecatedPasswordStorageSchemes(); 617 } 618 619 /** {@inheritDoc} */ 620 @Override 621 public DN getDN() 622 { 623 return passwordPolicySubentryDN; 624 } 625 626 /** {@inheritDoc} */ 627 @Override 628 public boolean isDefaultPasswordStorageScheme(String name) 629 { 630 return getDefaultPasswordPolicy().isDefaultPasswordStorageScheme(name); 631 } 632 633 /** {@inheritDoc} */ 634 @Override 635 public boolean isDeprecatedPasswordStorageScheme(String name) 636 { 637 return getDefaultPasswordPolicy().isDeprecatedPasswordStorageScheme(name); 638 } 639 640 /** {@inheritDoc} */ 641 @Override 642 public Collection<PasswordValidator<?>> getPasswordValidators() 643 { 644 if (!pValidatorNames.isEmpty()) 645 { 646 Collection<PasswordValidator<?>> values = new HashSet<>(); 647 for (DN validatorDN : pValidatorNames){ 648 PasswordValidator<?> validator = DirectoryServer.getPasswordValidator(validatorDN); 649 if (validator == null) { 650 PasswordValidator<?> errorValidator = new RejectPasswordValidator( 651 validatorDN.toString(), passwordPolicySubentryDN.toString()); 652 values.clear(); 653 values.add(errorValidator); 654 return values; 655 } 656 values.add(validator); 657 } 658 isAlreadyLogged.set(false); 659 return values; 660 } 661 return getDefaultPasswordPolicy().getPasswordValidators(); 662 } 663 664 665 /** 666 * Implementation of a specific Password Validator that reject all 667 * password due to mis-configured password policy subentry. 668 * This is only used when a subentry is referencing a password 669 * validator that is no longer configured. 670 */ 671 private final class RejectPasswordValidator extends 672 PasswordValidator<PasswordValidatorCfg> 673 { 674 private final String validatorName; 675 private final String pwPolicyName; 676 public RejectPasswordValidator(String name, String policyName) 677 { 678 super(); 679 validatorName = name; 680 pwPolicyName = policyName; 681 } 682 683 /** {@inheritDoc} */ 684 @Override 685 public void initializePasswordValidator(PasswordValidatorCfg configuration) 686 throws ConfigException, InitializationException 687 { 688 // do nothing 689 } 690 691 /** {@inheritDoc} */ 692 @Override 693 public boolean passwordIsAcceptable(ByteString newPassword, 694 Set<ByteString> currentPasswords, 695 Operation operation, Entry userEntry, 696 LocalizableMessageBuilder invalidReason) 697 { 698 invalidReason.append(ERR_PWPOLICY_REJECT_DUE_TO_UNKNOWN_VALIDATOR_REASON 699 .get()); 700 701 // Only log an error once, on first error 702 if (isAlreadyLogged.compareAndSet(false, true)) { 703 logger.error(ERR_PWPOLICY_REJECT_DUE_TO_UNKNOWN_VALIDATOR_LOG, 704 userEntry.getName(), pwPolicyName, validatorName); 705 } 706 return false; 707 } 708 } 709 710 /** {@inheritDoc} */ 711 @Override 712 public Collection<AccountStatusNotificationHandler<?>> 713 getAccountStatusNotificationHandlers() 714 { 715 return getDefaultPasswordPolicy().getAccountStatusNotificationHandlers(); 716 } 717 718 /** {@inheritDoc} */ 719 @Override 720 public PasswordGenerator<?> getPasswordGenerator() 721 { 722 return getDefaultPasswordPolicy().getPasswordGenerator(); 723 } 724 725}