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     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
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     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.core;
028    
029    
030    
031    import java.text.SimpleDateFormat;
032    import java.util.Date;
033    import java.util.Iterator;
034    import java.util.LinkedHashMap;
035    import java.util.LinkedHashSet;
036    import java.util.LinkedList;
037    import java.util.SortedSet;
038    import java.util.TimeZone;
039    import java.util.concurrent.ConcurrentHashMap;
040    import java.util.concurrent.CopyOnWriteArrayList;
041    import java.util.concurrent.CopyOnWriteArraySet;
042    
043    import org.opends.messages.Message;
044    import org.opends.server.admin.std.meta.PasswordPolicyCfgDefn;
045    import org.opends.server.admin.std.server.PasswordPolicyCfg;
046    import org.opends.server.admin.std.server.PasswordValidatorCfg;
047    import org.opends.server.api.AccountStatusNotificationHandler;
048    import org.opends.server.api.PasswordGenerator;
049    import org.opends.server.api.PasswordStorageScheme;
050    import org.opends.server.api.PasswordValidator;
051    import org.opends.server.config.ConfigException;
052    import org.opends.server.loggers.debug.DebugTracer;
053    import org.opends.server.protocols.asn1.ASN1OctetString;
054    import org.opends.server.schema.GeneralizedTimeSyntax;
055    import org.opends.server.types.AttributeType;
056    import org.opends.server.types.ByteString;
057    import org.opends.server.types.DebugLogLevel;
058    import org.opends.server.types.DN;
059    import org.opends.server.types.InitializationException;
060    
061    import static org.opends.messages.CoreMessages.*;
062    import static org.opends.server.config.ConfigConstants.*;
063    import static org.opends.server.loggers.debug.DebugLogger.*;
064    import static org.opends.server.schema.SchemaConstants.*;
065    import static org.opends.server.util.ServerConstants.*;
066    import static org.opends.server.util.StaticUtils.*;
067    
068    
069    
070    /**
071     * This class defines a data structure that holds information about a Directory
072     * Server password policy.
073     */
074    public class PasswordPolicy
075    {
076      /**
077       * The tracer object for the debug logger.
078       */
079      private static final DebugTracer TRACER = getTracer();
080    
081      // The DN of the entry containing the configuration for this password
082      // policy.
083      private final DN configEntryDN;
084    
085      // The attribute type that will hold user passwords for this password policy.
086      private final AttributeType passwordAttribute;
087    
088      // Indicates whether the attribute type uses the authPassword syntax.
089      private final boolean authPasswordSyntax;
090    
091      // Indicates whether a user with an expired password will still be allowed to
092      // change it via the password modify extended operation.
093      private boolean allowExpiredPasswordChanges =
094           DEFAULT_PWPOLICY_ALLOW_EXPIRED_CHANGES;
095    
096      // Indicates whether the password attribute will be allowed to have multiple
097      // distinct values.
098      private boolean allowMultiplePasswordValues =
099           DEFAULT_PWPOLICY_ALLOW_MULTIPLE_PW_VALUES;
100    
101      // Indicates whether to allow pre-encoded passwords.
102      private boolean allowPreEncodedPasswords =
103           DEFAULT_PWPOLICY_ALLOW_PRE_ENCODED_PASSWORDS;
104    
105      // Indicates whether users will be allowed to change their passwords.
106      private boolean allowUserPasswordChanges =
107           DEFAULT_PWPOLICY_ALLOW_USER_CHANGE;
108    
109      // Indicates whether to allow a password to expire without ever providing the
110      // user with a notification.
111      private boolean expirePasswordsWithoutWarning =
112           DEFAULT_PWPOLICY_EXPIRE_WITHOUT_WARNING;
113    
114      // Indicates whether users must change their passwords the first time they
115      // authenticate after their account is created.
116      private boolean forceChangeOnAdd =
117           DEFAULT_PWPOLICY_FORCE_CHANGE_ON_ADD;
118    
119      // Indicates whether a user must change their password after it has been reset
120      // by an administrator.
121      private boolean forceChangeOnReset =
122           DEFAULT_PWPOLICY_FORCE_CHANGE_ON_RESET;
123    
124      // Indicates whether a user must provide their current password in order to
125      // use a new password.
126      private boolean requireCurrentPassword =
127           DEFAULT_PWPOLICY_REQUIRE_CURRENT_PASSWORD;
128    
129      // Indicates whether users will be required to authenticate using a secure
130      // mechanism.
131      private boolean requireSecureAuthentication =
132           DEFAULT_PWPOLICY_REQUIRE_SECURE_AUTHENTICATION;
133    
134      // Indicates whether users will be required to change their passwords using a
135      // secure mechanism.
136      private boolean requireSecurePasswordChanges =
137           DEFAULT_PWPOLICY_REQUIRE_SECURE_PASSWORD_CHANGES;
138    
139      // Indicates whether password validation should be performed for
140      // administrative password changes.
141      private boolean skipValidationForAdministrators =
142           DEFAULT_PWPOLICY_SKIP_ADMIN_VALIDATION;
143    
144      // The set of account status notification handlers for this password policy.
145      private ConcurrentHashMap<DN, AccountStatusNotificationHandler>
146        notificationHandlers;
147    
148      // The set of password validators that will be used with this
149      // password policy.
150      private ConcurrentHashMap<DN, PasswordValidator<?>> passwordValidators;
151    
152      // The set of default password storage schemes for this password
153      // policy.
154      private CopyOnWriteArrayList<PasswordStorageScheme> defaultStorageSchemes =
155           new CopyOnWriteArrayList<PasswordStorageScheme>();
156      {
157        PasswordStorageScheme defaultScheme =
158          DirectoryServer.getPasswordStorageScheme(DEFAULT_PASSWORD_STORAGE_SCHEME);
159        if (defaultScheme != null) defaultStorageSchemes.add(defaultScheme);
160      }
161    
162      // The names of the deprecated password storage schemes for this password
163      // policy.
164      private CopyOnWriteArraySet<String> deprecatedStorageSchemes =
165           new CopyOnWriteArraySet<String>();
166    
167      // The DN of the password validator for this password policy.
168      private DN passwordGeneratorDN = null;
169    
170      // The password generator for use with this password policy.
171      private PasswordGenerator passwordGenerator = null;
172    
173      // The number of grace logins that a user may have.
174      private int graceLoginCount = DEFAULT_PWPOLICY_GRACE_LOGIN_COUNT;
175    
176      // The number of passwords to keep in the history.
177      private int historyCount = DEFAULT_PWPOLICY_HISTORY_COUNT;
178    
179      // The maximum length of time in seconds to keep passwords in the history.
180      private int historyDuration = DEFAULT_PWPOLICY_HISTORY_DURATION;
181    
182      // The maximum length of time in seconds that an account may remain idle
183      // before it is locked out.
184      private int idleLockoutInterval = DEFAULT_PWPOLICY_IDLE_LOCKOUT_INTERVAL;
185    
186      // The length of time a user should stay locked out, in seconds.
187      private int lockoutDuration = DEFAULT_PWPOLICY_LOCKOUT_DURATION;
188    
189      // The number of authentication failures before an account is locked out.
190      private int lockoutFailureCount = DEFAULT_PWPOLICY_LOCKOUT_FAILURE_COUNT;
191    
192      // The length of time that authentication failures should be counted against
193      // a user.
194      private int lockoutFailureExpirationInterval =
195           DEFAULT_PWPOLICY_LOCKOUT_FAILURE_EXPIRATION_INTERVAL;
196    
197      // The maximum password age (i.e., expiration interval), in seconds.
198      private int maximumPasswordAge = DEFAULT_PWPOLICY_MAXIMUM_PASSWORD_AGE;
199    
200      // The maximum password age for administratively reset passwords, in seconds.
201      private int maximumPasswordResetAge =
202           DEFAULT_PWPOLICY_MAXIMUM_PASSWORD_RESET_AGE;
203    
204      // The minimum password age, in seconds.
205      private int minimumPasswordAge = DEFAULT_PWPOLICY_MINIMUM_PASSWORD_AGE;
206    
207      // The password expiration warning interval, in seconds.
208      private int warningInterval = DEFAULT_PWPOLICY_WARNING_INTERVAL;
209    
210      // The the time by which all users will be required to change their passwords.
211      private long requireChangeByTime = -1L;
212    
213      // The attribute type that will hold the last login time.
214      private AttributeType lastLoginTimeAttribute = null;
215    
216      // The format string to use when generating the last login time.
217      private String lastLoginTimeFormat = null;
218    
219      // The set of previous last login time format strings.
220      private CopyOnWriteArrayList<String> previousLastLoginTimeFormats =
221           new CopyOnWriteArrayList<String>();
222    
223      // The state update failure policy.
224      private PasswordPolicyCfgDefn.StateUpdateFailurePolicy
225           stateUpdateFailurePolicy =
226                PasswordPolicyCfgDefn.StateUpdateFailurePolicy.REACTIVE;
227    
228    
229    
230      /**
231       * Creates a new password policy based on the configuration contained in the
232       * provided configuration entry.  Any parameters not included in the provided
233       * configuration entry will be assigned server-wide default values.
234       *
235       * @param  configuration  The configuration with the information to use to
236       *                      initialize this password policy.
237       *
238       * @throws  ConfigException  If the provided entry does not contain a valid
239       *                           password policy configuration.
240       *
241       * @throws  InitializationException  If an error occurs while initializing the
242       *                                   password policy that is not related to
243       *                                   the server configuration.
244       */
245      public PasswordPolicy(PasswordPolicyCfg configuration)
246             throws ConfigException, InitializationException
247      {
248        // Create a list of units and values that we can use to represent time
249        // periods.
250        LinkedHashMap<String,Double> timeUnits = new LinkedHashMap<String,Double>();
251        timeUnits.put(TIME_UNIT_SECONDS_ABBR, 1D);
252        timeUnits.put(TIME_UNIT_SECONDS_FULL, 1D);
253        timeUnits.put(TIME_UNIT_MINUTES_ABBR, 60D);
254        timeUnits.put(TIME_UNIT_MINUTES_FULL, 60D);
255        timeUnits.put(TIME_UNIT_HOURS_ABBR, (double) (60 * 60));
256        timeUnits.put(TIME_UNIT_HOURS_FULL, (double) (60 * 60));
257        timeUnits.put(TIME_UNIT_DAYS_ABBR, (double) (60 * 60 * 24));
258        timeUnits.put(TIME_UNIT_DAYS_FULL, (double) (60 * 60 * 24));
259        timeUnits.put(TIME_UNIT_WEEKS_ABBR, (double) (60 * 60 * 24 * 7));
260        timeUnits.put(TIME_UNIT_WEEKS_FULL, (double) (60 * 60 * 24 * 7));
261    
262        this.configEntryDN = configuration.dn();
263    
264        // Get the password attribute.  If specified, it must have either the
265        // user password or auth password syntax.
266        passwordAttribute = configuration.getPasswordAttribute();
267        String syntaxOID = passwordAttribute.getSyntaxOID();
268        if (syntaxOID.equals(SYNTAX_AUTH_PASSWORD_OID))
269        {
270          authPasswordSyntax = true;
271        }
272        else if (syntaxOID.equals(SYNTAX_USER_PASSWORD_OID))
273        {
274          authPasswordSyntax = false;
275        }
276        else
277        {
278          String syntax = passwordAttribute.getSyntax().getSyntaxName();
279          if ((syntax == null) || (syntax.length() == 0))
280          {
281            syntax = syntaxOID;
282          }
283    
284          Message message = ERR_PWPOLICY_INVALID_PASSWORD_ATTRIBUTE_SYNTAX.
285              get(String.valueOf(configEntryDN), passwordAttribute.getNameOrOID(),
286                  String.valueOf(syntax));
287          throw new ConfigException(message);
288        }
289    
290    
291        // Get the default storage schemes.  They must all reference valid storage
292        // schemes that support the syntax for the specified password attribute.
293        SortedSet<DN> storageSchemeDNs =
294          configuration.getDefaultPasswordStorageSchemeDNs();
295        try
296        {
297          LinkedList<PasswordStorageScheme> schemes =
298            new LinkedList<PasswordStorageScheme>();
299          for (DN configEntryDN : storageSchemeDNs)
300          {
301            PasswordStorageScheme scheme =
302              DirectoryServer.getPasswordStorageScheme(configEntryDN);
303    
304            if (this.authPasswordSyntax &&
305                (! scheme.supportsAuthPasswordSyntax()))
306            {
307              Message message = ERR_PWPOLICY_SCHEME_DOESNT_SUPPORT_AUTH.get(
308                  String.valueOf(configEntryDN),
309                  this.passwordAttribute.getNameOrOID());
310              throw new ConfigException(message);
311            }
312    
313            schemes.add(scheme);
314          }
315    
316          this.defaultStorageSchemes =
317            new CopyOnWriteArrayList<PasswordStorageScheme>(schemes);
318        }
319        catch (ConfigException ce)
320        {
321          throw ce;
322        }
323        catch (Exception e)
324        {
325          if (debugEnabled())
326          {
327            TRACER.debugCaught(DebugLogLevel.ERROR, e);
328          }
329    
330          Message message = ERR_PWPOLICY_CANNOT_DETERMINE_DEFAULT_STORAGE_SCHEMES.
331              get(String.valueOf(configEntryDN), getExceptionMessage(e));
332          throw new InitializationException(message, e);
333        }
334    
335    
336        // Get the names of the deprecated storage schemes.
337        SortedSet<DN> deprecatedStorageSchemeDNs =
338          configuration.getDeprecatedPasswordStorageSchemeDNs();
339        try
340        {
341          LinkedHashSet<String> newDeprecatedStorageSchemes =
342            new LinkedHashSet<String>();
343          for (DN schemeDN : deprecatedStorageSchemeDNs)
344          {
345            PasswordStorageScheme scheme =
346              DirectoryServer.getPasswordStorageScheme(schemeDN);
347            if (this.authPasswordSyntax)
348            {
349              if (scheme.supportsAuthPasswordSyntax())
350              {
351                newDeprecatedStorageSchemes.add(
352                    scheme.getAuthPasswordSchemeName());
353              }
354              else
355              {
356                Message message = ERR_PWPOLICY_DEPRECATED_SCHEME_NOT_AUTH.get(
357                    String.valueOf(configEntryDN),
358                    String.valueOf(schemeDN));
359                throw new ConfigException(message);
360              }
361            }
362            else
363            {
364              newDeprecatedStorageSchemes.add(
365                  toLowerCase(scheme.getStorageSchemeName()));
366            }
367          }
368    
369          this.deprecatedStorageSchemes =
370            new CopyOnWriteArraySet<String>(newDeprecatedStorageSchemes);
371        }
372        catch (Exception e)
373        {
374          if (debugEnabled())
375          {
376            TRACER.debugCaught(DebugLogLevel.ERROR, e);
377          }
378    
379          Message message =
380              ERR_PWPOLICY_CANNOT_DETERMINE_DEPRECATED_STORAGE_SCHEMES.
381                get(String.valueOf(configEntryDN), getExceptionMessage(e));
382          throw new InitializationException(message, e);
383        }
384    
385    
386        // Get the password validators.
387        SortedSet<DN> passwordValidators = configuration.getPasswordValidatorDNs();
388        ConcurrentHashMap<DN, PasswordValidator<?>> validators =
389          new ConcurrentHashMap<DN, PasswordValidator<?>>();
390        for (DN validatorDN : passwordValidators)
391        {
392          validators.put(validatorDN,
393              DirectoryServer.getPasswordValidator(validatorDN));
394        }
395        this.passwordValidators = validators;
396    
397    
398        // Get the status notification handlers.
399        SortedSet<DN> statusNotificationHandlers =
400          configuration.getAccountStatusNotificationHandlerDNs();
401        ConcurrentHashMap<DN,AccountStatusNotificationHandler> handlers =
402          new ConcurrentHashMap<DN,AccountStatusNotificationHandler>();
403        for (DN handlerDN : statusNotificationHandlers)
404        {
405          AccountStatusNotificationHandler handler =
406            DirectoryServer.getAccountStatusNotificationHandler(handlerDN);
407          handlers.put(handlerDN, handler);
408        }
409        this.notificationHandlers = handlers;
410    
411    
412        // Determine whether to allow user password changes.
413        this.allowUserPasswordChanges = configuration.isAllowUserPasswordChanges();
414    
415        // Determine whether to require the current password for user changes.
416        this.requireCurrentPassword =
417          configuration.isPasswordChangeRequiresCurrentPassword();
418    
419        // Determine whether to force password changes on add.
420        this.forceChangeOnAdd = configuration.isForceChangeOnAdd();
421    
422        // Determine whether to force password changes on reset.
423        this.forceChangeOnReset = configuration.isForceChangeOnReset();
424    
425        // Determine whether to validate reset passwords.
426        this.skipValidationForAdministrators =
427          configuration.isSkipValidationForAdministrators();
428    
429        // Get the password generator.
430        DN passGenDN = configuration.getPasswordGeneratorDN() ;
431        if (passGenDN != null)
432        {
433          this.passwordGeneratorDN = passGenDN;
434          this.passwordGenerator = DirectoryServer.getPasswordGenerator(passGenDN);
435        }
436    
437    
438        // Determine whether to require secure authentication.
439        this.requireSecureAuthentication =
440          configuration.isRequireSecureAuthentication();
441    
442        // Determine whether to require secure password changes.
443        this.requireSecurePasswordChanges =
444          configuration.isRequireSecurePasswordChanges() ;
445    
446        // Determine whether to allow multiple password values.
447        this.allowMultiplePasswordValues =
448          configuration.isAllowMultiplePasswordValues();
449    
450        // Determine whether to allow pre-encoded passwords.
451        this.allowPreEncodedPasswords = configuration.isAllowPreEncodedPasswords();
452    
453        // Get the minimum password age.
454        this.minimumPasswordAge = (int) configuration.getMinPasswordAge();
455    
456        // Get the maximum password age.
457        this.maximumPasswordAge = (int) configuration.getMaxPasswordAge();
458    
459        // Get the maximum password reset age.
460        this.maximumPasswordResetAge = (int) configuration
461            .getMaxPasswordResetAge();
462    
463        // Get the warning interval.
464        this.warningInterval = (int) configuration
465            .getPasswordExpirationWarningInterval();
466    
467        // Determine whether to expire passwords without warning.
468        this.expirePasswordsWithoutWarning = configuration
469            .isExpirePasswordsWithoutWarning();
470    
471        // If the expire without warning option is disabled, then there must be a
472        // warning interval.
473        if ((! this.expirePasswordsWithoutWarning()) &&
474            (this.getWarningInterval() <= 0))
475        {
476          Message message =
477            ERR_PWPOLICY_MUST_HAVE_WARNING_IF_NOT_EXPIRE_WITHOUT_WARNING.
478                get(String.valueOf(configEntryDN));
479          throw new ConfigException(message);
480        }
481    
482        // Determine whether to allow user changes for expired passwords.
483        this.allowExpiredPasswordChanges = configuration
484            .isAllowExpiredPasswordChanges();
485    
486        // Get the grace login count.
487        this.graceLoginCount = configuration.getGraceLoginCount();
488    
489        // Get the lockout failure count.
490        this.lockoutFailureCount = configuration.getLockoutFailureCount();
491    
492        // Get the lockout duration.
493        this.lockoutDuration = (int) configuration.getLockoutDuration();
494    
495        // Get the lockout failure expiration interval.
496        this.lockoutFailureExpirationInterval = (int) configuration
497            .getLockoutFailureExpirationInterval();
498    
499        // Get the required change time.
500        String requireChangeBy = configuration.getRequireChangeByTime();
501        try
502        {
503          if (requireChangeBy != null)
504          {
505            ByteString valueString = new ASN1OctetString(requireChangeBy);
506    
507            GeneralizedTimeSyntax syntax =
508                 (GeneralizedTimeSyntax)
509                 DirectoryServer.getAttributeSyntax(SYNTAX_GENERALIZED_TIME_OID,
510                                                    false);
511    
512            if (syntax == null)
513            {
514              this.requireChangeByTime =
515                   GeneralizedTimeSyntax.decodeGeneralizedTimeValue(valueString);
516            }
517            else
518            {
519              valueString =
520                   syntax.getEqualityMatchingRule().normalizeValue(valueString);
521              this.requireChangeByTime =
522                   GeneralizedTimeSyntax.decodeGeneralizedTimeValue(valueString);
523            }
524          }
525        }
526        catch (Exception e)
527        {
528          if (debugEnabled())
529          {
530            TRACER.debugCaught(DebugLogLevel.ERROR, e);
531          }
532    
533          Message message = ERR_PWPOLICY_CANNOT_DETERMINE_REQUIRE_CHANGE_BY_TIME.
534              get(String.valueOf(configEntryDN), getExceptionMessage(e));
535          throw new InitializationException(message, e);
536        }
537    
538    
539        // Get the last login time attribute.  If specified, it must be defined in
540        // the server schema.  It does not need to have a generalized time syntax
541        // because the value that it will store will not necessarily conform to this
542        // format.
543        lastLoginTimeAttribute = configuration.getLastLoginTimeAttribute();
544    
545    
546        // Get the last login time format.  If specified, it must be a valid format
547        // string.
548        String formatString = configuration.getLastLoginTimeFormat();
549        try
550        {
551          if (formatString != null)
552          {
553            try
554            {
555              new SimpleDateFormat(formatString);
556            }
557            catch (Exception e)
558            {
559              if (debugEnabled())
560              {
561                TRACER.debugCaught(DebugLogLevel.ERROR, e);
562              }
563    
564              Message message = ERR_PWPOLICY_INVALID_LAST_LOGIN_TIME_FORMAT.get(
565                  String.valueOf(configEntryDN), String.valueOf(formatString));
566              throw new ConfigException(message);
567            }
568    
569            this.lastLoginTimeFormat = formatString;
570          }
571        }
572        catch (ConfigException ce)
573        {
574          throw ce;
575        }
576        catch (Exception e)
577        {
578          if (debugEnabled())
579          {
580            TRACER.debugCaught(DebugLogLevel.ERROR, e);
581          }
582    
583          Message message = ERR_PWPOLICY_CANNOT_DETERMINE_LAST_LOGIN_TIME_FORMAT.
584              get(String.valueOf(configEntryDN), getExceptionMessage(e));
585          throw new InitializationException(message, e);
586        }
587    
588    
589        // Get the previous last login time formats.  If specified, they must all
590        // be valid format strings.
591        SortedSet<String> formatStrings =
592          configuration.getPreviousLastLoginTimeFormat() ;
593        try
594        {
595          if (formatStrings != null)
596          {
597            for (String s : formatStrings)
598            {
599              try
600              {
601                new SimpleDateFormat(s);
602              }
603              catch (Exception e)
604              {
605                if (debugEnabled())
606                {
607                  TRACER.debugCaught(DebugLogLevel.ERROR, e);
608                }
609    
610                Message message =
611                  ERR_PWPOLICY_INVALID_PREVIOUS_LAST_LOGIN_TIME_FORMAT.
612                      get(String.valueOf(configEntryDN), String.valueOf(s));
613                throw new ConfigException(message);
614              }
615            }
616    
617            this.previousLastLoginTimeFormats =
618                 new CopyOnWriteArrayList<String>(formatStrings);
619          }
620        }
621        catch (ConfigException ce)
622        {
623          throw ce;
624        }
625        catch (Exception e)
626        {
627          if (debugEnabled())
628          {
629            TRACER.debugCaught(DebugLogLevel.ERROR, e);
630          }
631    
632          Message message =
633              ERR_PWPOLICY_CANNOT_DETERMINE_PREVIOUS_LAST_LOGIN_TIME_FORMAT.
634                get(String.valueOf(configEntryDN), getExceptionMessage(e));
635          throw new InitializationException(message, e);
636        }
637    
638    
639        // Get the idle lockout duration.
640        this.idleLockoutInterval = (int) configuration.getIdleLockoutInterval();
641    
642    
643        // Get the state update failure policy.
644        this.stateUpdateFailurePolicy = configuration.getStateUpdateFailurePolicy();
645    
646    
647        // Get the password history count and duration.
648        this.historyCount    = configuration.getPasswordHistoryCount();
649        this.historyDuration = (int) configuration.getPasswordHistoryDuration();
650    
651    
652        /*
653         *  Holistic validation.
654         */
655    
656        // Ensure that the password attribute was included in the configuration
657        // entry, since it is required.
658        if (passwordAttribute == null)
659        {
660          Message message =
661              ERR_PWPOLICY_NO_PASSWORD_ATTRIBUTE.get(String.valueOf(configEntryDN));
662          throw new ConfigException(message);
663        }
664    
665        // Ensure that at least one default password storage scheme was included in
666        // the configuration entry, since it is required.
667        if (defaultStorageSchemes.isEmpty())
668        {
669          Message message = ERR_PWPOLICY_NO_DEFAULT_STORAGE_SCHEMES.get(
670              String.valueOf(configEntryDN));
671          throw new ConfigException(message);
672        }
673    
674        // If both a maximum password age and a warning interval are provided, then
675        // ensure that the warning interval is less than the maximum age.  Further,
676        // if a minimum age is specified, then the sum of the minimum age and the
677        // warning interval should be less than the maximum age.
678        if (maximumPasswordAge > 0)
679        {
680          int warnInterval = Math.max(0, warningInterval);
681          if (minimumPasswordAge > 0)
682          {
683            if ((warnInterval + minimumPasswordAge) >= maximumPasswordAge)
684            {
685              Message message =
686                  ERR_PWPOLICY_MIN_AGE_PLUS_WARNING_GREATER_THAN_MAX_AGE.
687                    get(String.valueOf(configEntryDN));
688              throw new ConfigException(message);
689            }
690          }
691          else if (warnInterval >= maximumPasswordAge)
692          {
693            Message message = ERR_PWPOLICY_WARNING_INTERVAL_LARGER_THAN_MAX_AGE.get(
694                String.valueOf(configEntryDN));
695            throw new ConfigException(message);
696          }
697        }
698      }
699    
700    
701    
702      /**
703       * Retrieves the DN of the configuration entry to which this password policy
704       * corresponds.
705       *
706       * @return  The DN of the configuration entry.
707       */
708      public DN getConfigEntryDN()
709      {
710        return configEntryDN;
711      }
712    
713    
714    
715      /**
716       * Retrieves the attribute type used to store the password.
717       *
718       * @return  The attribute type used to store the password.
719       */
720      public AttributeType getPasswordAttribute()
721      {
722        return passwordAttribute;
723      }
724    
725    
726    
727      /**
728       * Indicates whether the associated password attribute uses the auth password
729       * syntax.
730       *
731       * @return  <CODE>true</CODE> if the associated password attribute uses the
732       *          auth password syntax, or <CODE>false</CODE> if not.
733       */
734      public boolean usesAuthPasswordSyntax()
735      {
736        return authPasswordSyntax;
737      }
738    
739    
740    
741      /**
742       * Retrieves the default set of password storage schemes that will be used for
743       * this password policy.  The returned set should not be modified by the
744       * caller.
745       *
746       * @return  The default set of password storage schemes that will be used for
747       *          this password policy.
748       */
749      public CopyOnWriteArrayList<PasswordStorageScheme> getDefaultStorageSchemes()
750      {
751        return defaultStorageSchemes;
752      }
753    
754    
755    
756      /**
757       * Indicates whether the specified storage scheme is a default scheme for this
758       * password policy.
759       *
760       * @param  name  The name of the password storage scheme for which to make the
761       *               determination.
762       *
763       * @return  <CODE>true</CODE> if the storage scheme is a default scheme for
764       *          this password policy, or <CODE>false</CODE> if not.
765       */
766      public boolean isDefaultStorageScheme(String name)
767      {
768        CopyOnWriteArrayList<PasswordStorageScheme> defaultSchemes =
769             getDefaultStorageSchemes();
770        if (defaultSchemes == null)
771        {
772          return false;
773        }
774    
775        for (PasswordStorageScheme s : defaultSchemes)
776        {
777          if (authPasswordSyntax)
778          {
779            if (s.getAuthPasswordSchemeName().equalsIgnoreCase(name))
780            {
781              return true;
782            }
783          }
784          else
785          {
786            if (s.getStorageSchemeName().equalsIgnoreCase(name))
787            {
788              return true;
789            }
790          }
791        }
792    
793    
794        return false;
795      }
796    
797    
798    
799      /**
800       * Retrieves the names of the password storage schemes that have been
801       * deprecated.  If an authenticating user has one or more of these deprecated
802       * storage schemes in use in their entry, then they will be removed and
803       * replaced with the passwords encoded in the default storage scheme(s).  The
804       * returned list should not be altered by the caller.
805       *
806       * @return  The names of the password storage schemes that have been
807       *          deprecated.
808       */
809      public CopyOnWriteArraySet<String> getDeprecatedStorageSchemes()
810      {
811        return deprecatedStorageSchemes;
812      }
813    
814    
815    
816      /**
817       * Indicates whether the specified storage scheme is deprecated.
818       *
819       * @param  name  The name of the password storage scheme for which to make the
820       *               determination.
821       *
822       * @return  <CODE>true</CODE> if the storage scheme is deprecated, or
823       *          <CODE>false</CODE> if not.
824       */
825      public boolean isDeprecatedStorageScheme(String name)
826      {
827        CopyOnWriteArraySet<String> deprecatedSchemes =
828             getDeprecatedStorageSchemes();
829        if (deprecatedSchemes == null)
830        {
831          return false;
832        }
833    
834        for (String s : deprecatedSchemes)
835        {
836          if (s.equalsIgnoreCase(name))
837          {
838            return true;
839          }
840        }
841    
842        return false;
843      }
844    
845    
846    
847      /**
848       * Retrieves the set of password validators for this password policy.  The
849       * returned list should not be altered by the caller.
850       *
851       * @return  The set of password validators for this password policy.
852       */
853      public ConcurrentHashMap<DN,
854                  PasswordValidator<? extends PasswordValidatorCfg>>
855                  getPasswordValidators()
856      {
857        return passwordValidators;
858      }
859    
860    
861    
862      /**
863       * Retrieves the set of account status notification handlers that should be
864       * used with this password policy.  The returned list should not be altered by
865       * the caller.
866       *
867       * @return  The set of account status notification handlers that should be
868       *          used with this password policy.
869       */
870      public ConcurrentHashMap<DN,AccountStatusNotificationHandler>
871                  getAccountStatusNotificationHandlers()
872      {
873        return notificationHandlers;
874      }
875    
876    
877    
878      /**
879       * Indicates whether end users will be allowed to change their own passwords
880       * (subject to access control restrictions).
881       *
882       * @return  <CODE>true</CODE> if users will be allowed to change their own
883       *          passwords, or <CODE>false</CODE> if not.
884       */
885      public boolean allowUserPasswordChanges()
886      {
887        return allowUserPasswordChanges;
888      }
889    
890    
891    
892      /**
893       * Indicates whether the end user must provide their current password (via the
894       * password modify extended operation) in order to set a new password.
895       *
896       * @return  <CODE>true</CODE> if the end user must provide their current
897       *          password in order to set a new password, or <CODE>false</CODE> if
898       *          they will not.
899       */
900      public boolean requireCurrentPassword()
901      {
902        return requireCurrentPassword;
903      }
904    
905    
906    
907      /**
908       * Indicates whether users will be required to change their passwords as soon
909       * as they authenticate after their accounts have been created.
910       *
911       * @return  <CODE>true</CODE> if users will be required to change their
912       *          passwords at the initial authentication, or <CODE>false</CODE> if
913       *          not.
914       */
915      public boolean forceChangeOnAdd()
916      {
917        return forceChangeOnAdd;
918      }
919    
920    
921    
922      /**
923       * Indicates whether a user will be required to change their password after it
924       * has been reset by an administrator.
925       *
926       * @return  <CODE>true</CODE> if a user will be required to change their
927       *          password after it has been reset by an administrator, or
928       *          <CODE>false</CODE> if they can continue using that password.
929       */
930      public boolean forceChangeOnReset()
931      {
932        return forceChangeOnReset;
933      }
934    
935    
936    
937      /**
938       * Indicates whether operations by administrators that specify a new password
939       * for a user (e.g., add, modify, or password modify) will be allowed to
940       * bypass the password validation process that will be required for user
941       * password changes.
942       *
943       * @return  <CODE>true</CODE> if administrators will be allowed to bypass the
944       *          validation checks, or <CODE>false</CODE> if not.
945       */
946      public boolean skipValidationForAdministrators()
947      {
948        return skipValidationForAdministrators;
949      }
950    
951    
952    
953      /**
954       * Retrieves the DN of the password validator configuration entry.
955       *
956       * @return  The DN of the password validator configuration entry.
957       */
958      public DN getPasswordGeneratorDN()
959      {
960        return passwordGeneratorDN;
961      }
962    
963    
964    
965      /**
966       * Retrieves the password generator that will be used with this password
967       * policy.
968       *
969       * @return  The password generator that will be used with this password
970       *          policy, or <CODE>null</CODE> if there is none.
971       */
972      public PasswordGenerator getPasswordGenerator()
973      {
974        return passwordGenerator;
975      }
976    
977    
978    
979      /**
980       * Retrieves the maximum number of previous passwords to maintain in the
981       * password history.
982       *
983       * @return  The maximum number of previous passwords to maintain in the
984       *          password history.
985       */
986      public int getPasswordHistoryCount()
987      {
988        return historyCount;
989      }
990    
991    
992    
993      /**
994       * Retrieves the maximum length of time in seconds that previous passwords
995       * should remain in the password history.
996       *
997       * @return  The maximum length of time in seconds that previous passwords
998       *          should remain in the password history.
999       */
1000      public int getPasswordHistoryDuration()
1001      {
1002        return historyDuration;
1003      }
1004    
1005    
1006    
1007      /**
1008       * Indicates whether users with this password policy will be required to
1009       * authenticate in a secure manner that does not expose their password.
1010       *
1011       *  @return  <CODE>true</CODE> if users with this password policy will be
1012       *           required to authenticate in a secure manner that does not expose
1013       *           their password, or <CODE>false</CODE> if they may authenticate in
1014       *           an insecure manner.
1015       */
1016      public boolean requireSecureAuthentication()
1017      {
1018        return requireSecureAuthentication;
1019      }
1020    
1021    
1022    
1023      /**
1024       * Indicates whether users with this password policy will be required to
1025       * change their passwords in a secure manner that does not expose the new
1026       * password.
1027       *
1028       * @return  <CODE>true</CODE> if users with this password policy will be
1029       *          required to change their passwords in a secure manner that does
1030       *          not expose the new password, or <CODE>false</CODE> if they may
1031       *          change their password in an insecure manner.
1032       */
1033      public boolean requireSecurePasswordChanges()
1034      {
1035        return requireSecurePasswordChanges;
1036      }
1037    
1038    
1039    
1040      /**
1041       * Indicates whether user entries will be allowed to have multiple distinct
1042       * values in the password attribute.
1043       *
1044       * @return  <CODE>true</CODE> if clients will be allowed to have multiple
1045       *          distinct password values, or <CODE>false</CODE> if not.
1046       */
1047      public boolean allowMultiplePasswordValues()
1048      {
1049        return allowMultiplePasswordValues;
1050      }
1051    
1052    
1053    
1054      /**
1055       * Indicates whether clients will be allowed to set pre-encoded passwords that
1056       * are already hashed and therefore cannot be validated for correctness.
1057       *
1058       * @return  <CODE>true</CODE> if clients will be allowed to set pre-encoded
1059       *          passwords that cannot be validated, or <CODE>false</CODE> if not.
1060       */
1061      public boolean allowPreEncodedPasswords()
1062      {
1063        return allowPreEncodedPasswords;
1064      }
1065    
1066    
1067    
1068      /**
1069       * Retrieves the minimum password age, which is the minimum length of time in
1070       * seconds that must elapse between user password changes.
1071       *
1072       * @return  The minimum password age, which is the minimum length of time in
1073       *          seconds that must elapse between user password changes, or zero if
1074       *          there is no minimum age.
1075       */
1076      public int getMinimumPasswordAge()
1077      {
1078        if (minimumPasswordAge <= 0)
1079        {
1080          return 0;
1081        }
1082    
1083        return minimumPasswordAge;
1084      }
1085    
1086    
1087    
1088      /**
1089       * Retrieves the maximum length of time in seconds that will be allowed to
1090       * pass between password changes before the password is expired.
1091       *
1092       * @return  The maximum length of time in seconds that will be allowed to pass
1093       *          between password changes before the password is expired, or zero
1094       *          if password expiration should not be used.
1095       */
1096      public int getMaximumPasswordAge()
1097      {
1098        if (maximumPasswordAge < 0)
1099        {
1100          return 0;
1101        }
1102    
1103        return maximumPasswordAge;
1104      }
1105    
1106    
1107    
1108      /**
1109       * Retrieves the maximum length of time in seconds that will be allowed to
1110       * pass after an administrative password reset before that password is
1111       * expired.
1112       *
1113       * @return  The maximum length of time in seconds that will be allowed to pass
1114       *          after an administrative password reset before that password is
1115       *          expired, or zero if there is no limit.
1116       */
1117      public int getMaximumPasswordResetAge()
1118      {
1119        if (maximumPasswordResetAge < 0)
1120        {
1121          return 0;
1122        }
1123    
1124        return maximumPasswordResetAge;
1125      }
1126    
1127    
1128    
1129      /**
1130       * Retrieves the maximum length of time in seconds before the password will
1131       * expire that the user should start receiving warning notifications.
1132       *
1133       * @return  The maximum length of time in seconds before the password will
1134       *          expire that the user should start receiving warning notifications,
1135       *          or zero if no warning should be given.
1136       */
1137      public int getWarningInterval()
1138      {
1139        if (warningInterval < 0)
1140        {
1141          return 0;
1142        }
1143    
1144        return warningInterval;
1145      }
1146    
1147    
1148    
1149      /**
1150       * Indicates whether user passwords will be allowed to expire without the
1151       * user receiving at least one notification during the warning period.
1152       *
1153       * @return  <CODE>true</CODE> if user passwords will be allowed to expire
1154       *          without the user receiving at least one notification during the
1155       *          warning period, or <CODE>false</CODE> if the user will always see
1156       *          at least one warning before the password expires.
1157       */
1158      public boolean expirePasswordsWithoutWarning()
1159      {
1160        return expirePasswordsWithoutWarning;
1161      }
1162    
1163    
1164    
1165      /**
1166       * Indicates whether a user will be allowed to change their password after it
1167       * expires and they have no remaining grace logins (and will not be allowed to
1168       * perform any other operation until the password is changed).
1169       *
1170       * @return  <CODE>true</CODE> if a user will be allowed to change their
1171       *          password after it expires and they have no remaining grace longs,
1172       *          or <CODE>false</CODE> if the account will be completely locked and
1173       *          the password must be reset by an administrator.
1174       */
1175      public boolean allowExpiredPasswordChanges()
1176      {
1177        return allowExpiredPasswordChanges;
1178      }
1179    
1180    
1181    
1182      /**
1183       * Retrieves the maximum number of grace logins that a user will be allowed
1184       * after their password has expired before they are completely locked out.
1185       *
1186       * @return  The maximum number of grace logins that a user will be allowed
1187       *          after their password has expired before they are completely
1188       *          locked out, or zero if no grace logins will be allowed or the
1189       *          grace login duration will be in effect instead of a fixed number
1190       *          of logins.
1191       */
1192      public int getGraceLoginCount()
1193      {
1194        if (graceLoginCount < 0)
1195        {
1196          return 0;
1197        }
1198    
1199        return graceLoginCount;
1200      }
1201    
1202    
1203    
1204      /**
1205       * Retrieves the maximum number of authentication failures that will be
1206       * allowed before an account is locked out.
1207       *
1208       * @return  The maximum number of authentication failures that will be allowed
1209       *          before an account is locked out, or zero if no account lockout
1210       *          will be in effect.
1211       */
1212      public int getLockoutFailureCount()
1213      {
1214        if (lockoutFailureCount < 0)
1215        {
1216          return 0;
1217        }
1218    
1219        return lockoutFailureCount;
1220      }
1221    
1222    
1223    
1224      /**
1225       * Retrieves the maximum length of time in seconds that an account will be
1226       * locked out due to too many failed authentication attempts.
1227       *
1228       * @return  The maximum length of time in seconds that an account will be
1229       *          locked out due to too many failed authentication attempts, or
1230       *          zero if the account will remain locked until explicitly unlocked
1231       *          by an administrator.
1232       */
1233      public int getLockoutDuration()
1234      {
1235        if (lockoutDuration < 0)
1236        {
1237          return 0;
1238        }
1239    
1240        return lockoutDuration;
1241      }
1242    
1243    
1244    
1245      /**
1246       * Retrieves the maximum length of time in seconds that an authentication
1247       * failure will be held against a user before it is removed from the failed
1248       * login count.
1249       *
1250       * @return  The maximum length of time in seconds that an authentication
1251       *          failure will be held against a user before it is removed from the
1252       *          failed login count, or zero if authentication failures will never
1253       *          expire.
1254       */
1255      public int getLockoutFailureExpirationInterval()
1256      {
1257        if (lockoutFailureExpirationInterval < 0)
1258        {
1259          return 0;
1260        }
1261    
1262        return lockoutFailureExpirationInterval;
1263      }
1264    
1265    
1266    
1267      /**
1268       * Retrieves the time by which all users will be required to change their
1269       * passwords, expressed in the number of milliseconds since midnight of
1270       * January 1, 1970 (i.e., the zero time for
1271       * <CODE>System.currentTimeMillis()</CODE>).  Any passwords not changed before
1272       * this time will automatically enter a state in which they must be changed
1273       * before any other operation will be allowed.
1274       *
1275       * @return  The time by which all users will be required to change their
1276       *          passwords, or zero if no such constraint is in effect.
1277       */
1278      public long getRequireChangeByTime()
1279      {
1280        if (requireChangeByTime < 0)
1281        {
1282          return 0;
1283        }
1284    
1285        return requireChangeByTime;
1286      }
1287    
1288    
1289    
1290      /**
1291       * Retrieves the attribute type used to store the last login time.
1292       *
1293       * @return  The attribute type used to store the last login time, or
1294       *          <CODE>null</CODE> if the last login time is not to be maintained.
1295       */
1296      public AttributeType getLastLoginTimeAttribute()
1297      {
1298        return lastLoginTimeAttribute;
1299      }
1300    
1301    
1302    
1303      /**
1304       * Retrieves the format string that should be used for the last login time.
1305       *
1306       * @return  The format string that should be used to for the last login time,
1307       *          or <CODE>null</CODE> if the last login time is not to be
1308       *          maintained.
1309       */
1310      public String getLastLoginTimeFormat()
1311      {
1312        return lastLoginTimeFormat;
1313      }
1314    
1315    
1316    
1317      /**
1318       * Retrieves the list of previous last login time formats that might have been
1319       * used for users associated with this password policy.
1320       *
1321       * @return  The list of previous last login time formats that might have been
1322       *          used for users associated with this password policy.
1323       */
1324      public CopyOnWriteArrayList<String> getPreviousLastLoginTimeFormats()
1325      {
1326        return previousLastLoginTimeFormats;
1327      }
1328    
1329    
1330    
1331      /**
1332       * Retrieves the maximum length of time in seconds that an account will be
1333       * allowed to remain idle (no authentications performed as the user) before it
1334       * will be locked out.
1335       *
1336       * @return  The maximum length of time in seconds that an account will be
1337       *          allowed to remain idle before it will be locked out.
1338       */
1339      public int getIdleLockoutInterval()
1340      {
1341        if (idleLockoutInterval < 0)
1342        {
1343          return 0;
1344        }
1345    
1346        return idleLockoutInterval;
1347      }
1348    
1349    
1350    
1351      /**
1352       * Retrieves the state update failure policy for this password policy.
1353       *
1354       * @return  The state update failure policy for this password policy.
1355       */
1356      public PasswordPolicyCfgDefn.StateUpdateFailurePolicy
1357                  getStateUpdateFailurePolicy()
1358      {
1359        return stateUpdateFailurePolicy;
1360      }
1361    
1362    
1363    
1364      /**
1365       * Retrieves a string representation of this password policy.
1366       *
1367       * @return  A string representation of this password policy.
1368       */
1369      public String toString()
1370      {
1371        StringBuilder buffer = new StringBuilder();
1372        toString(buffer);
1373        return buffer.toString();
1374      }
1375    
1376    
1377    
1378      /**
1379       * Appends a string representation of this password policy to the provided
1380       * buffer.
1381       *
1382       * @param  buffer  The buffer to which the information should be appended.
1383       */
1384      public void toString(StringBuilder buffer)
1385      {
1386        buffer.append("Password Attribute:                    ");
1387        buffer.append(passwordAttribute.getNameOrOID());
1388        buffer.append(EOL);
1389    
1390        buffer.append("Default Password Storage Schemes:      ");
1391        if ((defaultStorageSchemes == null) || defaultStorageSchemes.isEmpty())
1392        {
1393          buffer.append("{none specified}");
1394          buffer.append(EOL);
1395        }
1396        else
1397        {
1398          Iterator<PasswordStorageScheme> iterator =
1399               defaultStorageSchemes.iterator();
1400          buffer.append(iterator.next().getStorageSchemeName());
1401          buffer.append(EOL);
1402    
1403          while (iterator.hasNext())
1404          {
1405            buffer.append("                                       ");
1406            buffer.append(iterator.next().getStorageSchemeName());
1407            buffer.append(EOL);
1408          }
1409        }
1410    
1411        buffer.append("Deprecated Password Storage Schemes:   ");
1412        if ((deprecatedStorageSchemes == null) ||
1413            deprecatedStorageSchemes.isEmpty())
1414        {
1415          buffer.append("{none specified}");
1416          buffer.append(EOL);
1417        }
1418        else
1419        {
1420          Iterator<String> iterator = deprecatedStorageSchemes.iterator();
1421          buffer.append(iterator.next());
1422          buffer.append(EOL);
1423    
1424          while (iterator.hasNext())
1425          {
1426            buffer.append("                                       ");
1427            buffer.append(iterator.next());
1428            buffer.append(EOL);
1429          }
1430        }
1431    
1432        buffer.append("Allow Multiple Password Values:        ");
1433        buffer.append(allowMultiplePasswordValues);
1434        buffer.append(EOL);
1435    
1436        buffer.append("Allow Pre-Encoded Passwords:           ");
1437        buffer.append(allowPreEncodedPasswords);
1438        buffer.append(EOL);
1439    
1440        buffer.append("Allow User Password Changes:           ");
1441        buffer.append(allowUserPasswordChanges);
1442        buffer.append(EOL);
1443    
1444        buffer.append("Force Password Change on Add:          ");
1445        buffer.append(forceChangeOnAdd);
1446        buffer.append(EOL);
1447    
1448        buffer.append("Force Password Change on Admin Reset:  ");
1449        buffer.append(forceChangeOnReset);
1450        buffer.append(EOL);
1451    
1452        buffer.append("Require Current Password:              ");
1453        buffer.append(requireCurrentPassword);
1454        buffer.append(EOL);
1455    
1456        buffer.append("Require Secure Authentication:         ");
1457        buffer.append(requireSecureAuthentication);
1458        buffer.append(EOL);
1459    
1460        buffer.append("Require Secure Password Changes:       ");
1461        buffer.append(requireSecurePasswordChanges);
1462        buffer.append(EOL);
1463    
1464        buffer.append("Lockout Failure Expiration Interval:   ");
1465        buffer.append(lockoutFailureExpirationInterval);
1466        buffer.append(" seconds");
1467        buffer.append(EOL);
1468    
1469        buffer.append("Password Validators:                   ");
1470        if ((passwordValidators == null) || passwordValidators.isEmpty())
1471        {
1472          buffer.append("{none specified}");
1473          buffer.append(EOL);
1474        }
1475        else
1476        {
1477          Iterator<DN> iterator = passwordValidators.keySet().iterator();
1478          iterator.next().toString(buffer);
1479          buffer.append(EOL);
1480    
1481          while (iterator.hasNext())
1482          {
1483            buffer.append("                                       ");
1484            iterator.next().toString(buffer);
1485            buffer.append(EOL);
1486          }
1487        }
1488    
1489        buffer.append("Skip Validation for Administrators:    ");
1490        buffer.append(skipValidationForAdministrators);
1491        buffer.append(EOL);
1492    
1493        buffer.append("Password Generator:                    ");
1494        if (passwordGenerator == null)
1495        {
1496          buffer.append("{none specified}");
1497        }
1498        else
1499        {
1500          passwordGeneratorDN.toString(buffer);
1501        }
1502        buffer.append(EOL);
1503    
1504        buffer.append("Account Status Notification Handlers:  ");
1505        if ((notificationHandlers == null) || notificationHandlers.isEmpty())
1506        {
1507          buffer.append("{none specified}");
1508          buffer.append(EOL);
1509        }
1510        else
1511        {
1512          Iterator<DN> iterator = notificationHandlers.keySet().iterator();
1513          iterator.next().toString(buffer);
1514          buffer.append(EOL);
1515    
1516          while (iterator.hasNext())
1517          {
1518            buffer.append("                                       ");
1519            iterator.next().toString(buffer);
1520            buffer.append(EOL);
1521          }
1522        }
1523    
1524        buffer.append("Minimum Password Age:                  ");
1525        buffer.append(minimumPasswordAge);
1526        buffer.append(" seconds");
1527        buffer.append(EOL);
1528    
1529        buffer.append("Maximum Password Age:                  ");
1530        buffer.append(maximumPasswordAge);
1531        buffer.append(" seconds");
1532        buffer.append(EOL);
1533    
1534        buffer.append("Maximum Password Reset Age:            ");
1535        buffer.append(maximumPasswordResetAge);
1536        buffer.append(" seconds");
1537        buffer.append(EOL);
1538    
1539        buffer.append("Expiration Warning Interval:           ");
1540        buffer.append(warningInterval);
1541        buffer.append(" seconds");
1542        buffer.append(EOL);
1543    
1544        buffer.append("Expire Passwords Without Warning:      ");
1545        buffer.append(expirePasswordsWithoutWarning);
1546        buffer.append(EOL);
1547    
1548        buffer.append("Allow Expired Password Changes:        ");
1549        buffer.append(allowExpiredPasswordChanges);
1550        buffer.append(EOL);
1551    
1552        buffer.append("Grace Login Count:                     ");
1553        buffer.append(graceLoginCount);
1554        buffer.append(EOL);
1555    
1556        buffer.append("Lockout Failure Count:                 ");
1557        buffer.append(lockoutFailureCount);
1558        buffer.append(EOL);
1559    
1560        buffer.append("Lockout Duration:                      ");
1561        buffer.append(lockoutDuration);
1562        buffer.append(" seconds");
1563        buffer.append(EOL);
1564    
1565        buffer.append("Lockout Count Expiration Interval:     ");
1566        buffer.append(lockoutFailureExpirationInterval);
1567        buffer.append(" seconds");
1568        buffer.append(EOL);
1569    
1570        buffer.append("Required Password Change By Time:      ");
1571        if (requireChangeByTime <= 0)
1572        {
1573          buffer.append("{none specified}");
1574        }
1575        else
1576        {
1577          SimpleDateFormat dateFormat =
1578               new SimpleDateFormat(DATE_FORMAT_GENERALIZED_TIME);
1579          dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
1580          buffer.append(dateFormat.format(new Date(requireChangeByTime)));
1581        }
1582        buffer.append(EOL);
1583    
1584        buffer.append("Last Login Time Attribute:             ");
1585        if (lastLoginTimeAttribute == null)
1586        {
1587          buffer.append("{none specified}");
1588        }
1589        else
1590        {
1591          buffer.append(lastLoginTimeAttribute.getNameOrOID());
1592        }
1593        buffer.append(EOL);
1594    
1595        buffer.append("Last Login Time Format:                ");
1596        if (lastLoginTimeFormat == null)
1597        {
1598          buffer.append("{none specified}");
1599        }
1600        else
1601        {
1602          buffer.append(lastLoginTimeFormat);
1603        }
1604        buffer.append(EOL);
1605    
1606        buffer.append("Previous Last Login Time Formats:      ");
1607        if ((previousLastLoginTimeFormats == null) ||
1608            previousLastLoginTimeFormats.isEmpty())
1609        {
1610          buffer.append("{none specified}");
1611          buffer.append(EOL);
1612        }
1613        else
1614        {
1615          Iterator<String> iterator = previousLastLoginTimeFormats.iterator();
1616    
1617          buffer.append(iterator.next());
1618          buffer.append(EOL);
1619    
1620          while (iterator.hasNext())
1621          {
1622            buffer.append("                                       ");
1623            buffer.append(iterator.next());
1624            buffer.append(EOL);
1625          }
1626        }
1627    
1628        buffer.append("Idle Lockout Interval:                 ");
1629        buffer.append(idleLockoutInterval);
1630        buffer.append(" seconds");
1631        buffer.append(EOL);
1632    
1633        buffer.append("History Count:                         ");
1634        buffer.append(historyCount);
1635        buffer.append(EOL);
1636    
1637        buffer.append("Update Failure Policy:                 ");
1638        buffer.append(stateUpdateFailurePolicy.toString());
1639        buffer.append(EOL);
1640      }
1641    }
1642