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 2006-2008 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.util.ServerConstants.*;
022import static org.opends.server.util.StaticUtils.*;
023
024import java.text.SimpleDateFormat;
025import java.util.Collection;
026import java.util.Date;
027import java.util.HashMap;
028import java.util.Iterator;
029import java.util.LinkedHashSet;
030import java.util.LinkedList;
031import java.util.List;
032import java.util.Map;
033import java.util.Set;
034import java.util.SortedSet;
035import java.util.TimeZone;
036
037import org.forgerock.i18n.LocalizableMessage;
038import org.forgerock.i18n.slf4j.LocalizedLogger;
039import org.forgerock.opendj.config.server.ConfigChangeResult;
040import org.forgerock.opendj.config.server.ConfigException;
041import org.forgerock.opendj.config.server.ConfigurationChangeListener;
042import org.forgerock.opendj.ldap.ByteString;
043import org.forgerock.opendj.ldap.DN;
044import org.forgerock.opendj.ldap.GeneralizedTime;
045import org.forgerock.opendj.ldap.ResultCode;
046import org.forgerock.opendj.ldap.schema.AttributeType;
047import org.forgerock.opendj.server.config.meta.PasswordPolicyCfgDefn.StateUpdateFailurePolicy;
048import org.forgerock.opendj.server.config.server.PasswordPolicyCfg;
049import org.opends.server.api.AccountStatusNotificationHandler;
050import org.opends.server.api.AuthenticationPolicyFactory;
051import org.opends.server.api.PasswordGenerator;
052import org.opends.server.api.PasswordStorageScheme;
053import org.opends.server.api.PasswordValidator;
054import org.opends.server.types.InitializationException;
055import org.opends.server.util.SchemaUtils;
056import org.opends.server.util.SchemaUtils.PasswordType;
057
058/**
059 * This class is the interface between the password policy configurable
060 * component and a password policy state object. When a password policy entry is
061 * added to the configuration, an instance of this class is created and
062 * registered to manage subsequent modification to that configuration entry,
063 * including validating any proposed modification and applying an accepted
064 * modification.
065 */
066public final class PasswordPolicyFactory implements
067    AuthenticationPolicyFactory<PasswordPolicyCfg>
068{
069  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
070
071  /** Password policy implementation. */
072  private static final class PasswordPolicyImpl extends PasswordPolicy
073      implements ConfigurationChangeListener<PasswordPolicyCfg>
074  {
075    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
076
077    /** Current configuration. */
078    private PasswordPolicyCfg configuration;
079
080    /** Indicates whether the attribute type uses the authPassword syntax. */
081    private boolean authPasswordSyntax;
082
083    /** The set of account status notification handlers for this password policy. */
084    private Map<DN, AccountStatusNotificationHandler<?>> notificationHandlers;
085    /** The set of password validators that will be used with this password policy. */
086    private Map<DN, PasswordValidator<?>> passwordValidators;
087
088    /** The set of default password storage schemes for this password policy. */
089    private List<PasswordStorageScheme<?>> defaultStorageSchemes;
090    /** The names of the deprecated password storage schemes for this password policy. */
091    private Set<String> deprecatedStorageSchemes;
092
093    /** The password generator for use with this password policy. */
094    private PasswordGenerator<?> passwordGenerator;
095
096    /** The the time by which all users will be required to change their passwords. */
097    private long requireChangeByTime;
098
099    private final ServerContext serverContext;
100
101    @Override
102    public void finalizeAuthenticationPolicy()
103    {
104      configuration.removePasswordPolicyChangeListener(this);
105    }
106
107    @Override
108    public ConfigChangeResult applyConfigurationChange(PasswordPolicyCfg configuration)
109    {
110      final ConfigChangeResult ccr = new ConfigChangeResult();
111      try
112      {
113        updateConfiguration(configuration, true);
114      }
115      catch (ConfigException ce)
116      {
117        ccr.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
118        ccr.addMessage(ERR_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG.get(configuration.dn(), ce.getMessage()));
119      }
120      catch (InitializationException ie)
121      {
122        ccr.addMessage(ERR_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG.get(
123            configuration.dn(), ie.getMessage()));
124        ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
125      }
126      catch (Exception e)
127      {
128        ccr.addMessage(ERR_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG.get(
129            configuration.dn(), stackTraceToSingleLineString(e)));
130        ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
131      }
132      return ccr;
133    }
134
135    @Override
136    public boolean isConfigurationChangeAcceptable(
137        PasswordPolicyCfg configuration, List<LocalizableMessage> unacceptableReasons)
138    {
139      try
140      {
141        updateConfiguration(configuration, false);
142      }
143      catch (ConfigException | InitializationException e)
144      {
145        LocalizableMessage message = ERR_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG.get(
146            configuration.dn(), e.getMessage());
147        unacceptableReasons.add(message);
148        return false;
149      }
150      catch (Exception e)
151      {
152        LocalizableMessage message = ERR_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG
153            .get(configuration.dn(), stackTraceToSingleLineString(e));
154        unacceptableReasons.add(message);
155        return false;
156      }
157
158      // If we've gotten here, then it is acceptable.
159      return true;
160    }
161
162    /**
163     * Creates a new password policy based on the configuration contained in the
164     * provided configuration entry. Any parameters not included in the provided
165     * configuration entry will be assigned server-wide default values.
166     * @param serverContext TODO
167     * @param configuration
168     *          The configuration with the information to use to initialize this
169     *          password policy.
170     *
171     * @throws ConfigException
172     *           If the provided entry does not contain a valid password policy
173     *           configuration.
174     * @throws InitializationException
175     *           If an error occurs while initializing the password policy that
176     *           is not related to the server configuration.
177     */
178    private PasswordPolicyImpl(ServerContext serverContext, PasswordPolicyCfg configuration)
179        throws ConfigException, InitializationException
180    {
181      this.serverContext = serverContext;
182      updateConfiguration(configuration, true);
183    }
184
185    private void updateConfiguration(PasswordPolicyCfg configuration,
186        boolean applyChanges) throws ConfigException,
187        InitializationException
188    {
189      final DN configEntryDN = configuration.dn();
190
191      // Get the password attribute. If specified, it must have either the
192      // user password or auth password syntax.
193      final AttributeType passwordAttribute = configuration.getPasswordAttribute();
194      final PasswordType passwordType = SchemaUtils.checkPasswordType(passwordAttribute);
195      if (PasswordType.AUTH_PASSWORD.equals(passwordType))
196      {
197        authPasswordSyntax = true;
198      }
199      else if (PasswordType.USER_PASSWORD.equals(passwordType))
200      {
201        authPasswordSyntax = false;
202      }
203      else
204      {
205        String syntax = passwordAttribute.getSyntax().getName();
206        if (syntax == null || syntax.length() == 0)
207        {
208          syntax = passwordAttribute.getSyntax().getOID();
209        }
210
211        throw new ConfigException(ERR_PWPOLICY_INVALID_PASSWORD_ATTRIBUTE_SYNTAX.get(
212            configEntryDN, passwordAttribute.getNameOrOID(), syntax));
213      }
214
215      // Get the default storage schemes. They must all reference valid storage
216      // schemes that support the syntax for the specified password attribute.
217      List<PasswordStorageScheme<?>> defaultStorageSchemes = new LinkedList<>();
218      for (DN schemeDN : configuration.getDefaultPasswordStorageSchemeDNs())
219      {
220        PasswordStorageScheme<?> scheme = DirectoryServer
221            .getPasswordStorageScheme(schemeDN);
222
223        if (authPasswordSyntax && !scheme.supportsAuthPasswordSyntax())
224        {
225          throw new ConfigException(ERR_PWPOLICY_SCHEME_DOESNT_SUPPORT_AUTH.get(
226              schemeDN, passwordAttribute.getNameOrOID()));
227        }
228
229        defaultStorageSchemes.add(scheme);
230      }
231
232      // Get the names of the deprecated storage schemes.
233      Set<String> deprecatedStorageSchemes = new LinkedHashSet<>();
234      for (DN schemeDN : configuration.getDeprecatedPasswordStorageSchemeDNs())
235      {
236        PasswordStorageScheme<?> scheme = DirectoryServer
237            .getPasswordStorageScheme(schemeDN);
238        if (authPasswordSyntax)
239        {
240          if (scheme.supportsAuthPasswordSyntax())
241          {
242            deprecatedStorageSchemes.add(toLowerCase(scheme
243                .getAuthPasswordSchemeName()));
244          }
245          else
246          {
247            throw new ConfigException(ERR_PWPOLICY_DEPRECATED_SCHEME_NOT_AUTH.get(
248                configEntryDN, schemeDN));
249          }
250        }
251        else
252        {
253          deprecatedStorageSchemes.add(toLowerCase(scheme.getStorageSchemeName()));
254        }
255      }
256
257      // Get the password validators.
258      Map<DN, PasswordValidator<?>> passwordValidators = new HashMap<>();
259      for (DN validatorDN : configuration.getPasswordValidatorDNs())
260      {
261        passwordValidators.put(validatorDN,
262            DirectoryServer.getPasswordValidator(validatorDN));
263      }
264
265      // Get the status notification handlers.
266      Map<DN, AccountStatusNotificationHandler<?>> notificationHandlers = new HashMap<>();
267      for (DN handlerDN : configuration.getAccountStatusNotificationHandlerDNs())
268      {
269        AccountStatusNotificationHandler<?> handler = DirectoryServer
270            .getAccountStatusNotificationHandler(handlerDN);
271        notificationHandlers.put(handlerDN, handler);
272      }
273
274      // Get the password generator.
275      PasswordGenerator<?> passwordGenerator = null;
276      DN passGenDN = configuration.getPasswordGeneratorDN();
277      if (passGenDN != null)
278      {
279        passwordGenerator = DirectoryServer.getPasswordGenerator(passGenDN);
280      }
281
282      // If the expire without warning option is disabled, then there must be a
283      // warning interval.
284      if (!configuration.isExpirePasswordsWithoutWarning()
285          && configuration.getPasswordExpirationWarningInterval() <= 0)
286      {
287        LocalizableMessage message =
288          ERR_PWPOLICY_MUST_HAVE_WARNING_IF_NOT_EXPIRE_WITHOUT_WARNING.get(configEntryDN);
289        throw new ConfigException(message);
290      }
291
292      // Get the required change time.
293      String requireChangeBy = configuration.getRequireChangeByTime();
294      long requireChangeByTime = 0L;
295      try
296      {
297        if (requireChangeBy != null)
298        {
299          ByteString valueString = ByteString.valueOfUtf8(requireChangeBy);
300          requireChangeByTime = GeneralizedTime.valueOf(valueString.toString()).getTimeInMillis();
301        }
302      }
303      catch (Exception e)
304      {
305        logger.traceException(e);
306
307        LocalizableMessage message = ERR_PWPOLICY_CANNOT_DETERMINE_REQUIRE_CHANGE_BY_TIME
308            .get(configEntryDN, getExceptionMessage(e));
309        throw new InitializationException(message, e);
310      }
311
312      // Get the last login time format. If specified, it must be a valid format
313      // string.
314      String formatString = configuration.getLastLoginTimeFormat();
315      if (formatString != null)
316      {
317        try
318        {
319          new SimpleDateFormat(formatString);
320        }
321        catch (Exception e)
322        {
323          logger.traceException(e);
324          throw new ConfigException(ERR_PWPOLICY_INVALID_LAST_LOGIN_TIME_FORMAT.get(configEntryDN, formatString));
325        }
326      }
327
328      // Get the previous last login time formats. If specified, they must all
329      // be valid format strings.
330      SortedSet<String> formatStrings = configuration.getPreviousLastLoginTimeFormat();
331      if (formatStrings != null)
332      {
333        for (String s : formatStrings)
334        {
335          try
336          {
337            new SimpleDateFormat(s);
338          }
339          catch (Exception e)
340          {
341            logger.traceException(e);
342            throw new ConfigException(ERR_PWPOLICY_INVALID_PREVIOUS_LAST_LOGIN_TIME_FORMAT.get(configEntryDN, s));
343          }
344        }
345      }
346
347      // If both a maximum password age and a warning interval are provided,
348      // then
349      // ensure that the warning interval is less than the maximum age. Further,
350      // if a minimum age is specified, then the sum of the minimum age and the
351      // warning interval should be less than the maximum age.
352      if (configuration.getMaxPasswordAge() > 0)
353      {
354        long warnInterval = Math.max(0L,
355            configuration.getPasswordExpirationWarningInterval());
356        if (configuration.getMinPasswordAge() > 0)
357        {
358          if (warnInterval + configuration.getMinPasswordAge() >= configuration.getMaxPasswordAge())
359          {
360            LocalizableMessage message =
361              ERR_PWPOLICY_MIN_AGE_PLUS_WARNING_GREATER_THAN_MAX_AGE.get(configEntryDN);
362            throw new ConfigException(message);
363          }
364        }
365        else if (warnInterval >= configuration.getMaxPasswordAge())
366        {
367          LocalizableMessage message = ERR_PWPOLICY_WARNING_INTERVAL_LARGER_THAN_MAX_AGE.get(configEntryDN);
368          throw new ConfigException(message);
369        }
370      }
371
372      // If we've got this far then the configuration is good and we can commit
373      // the changes if required.
374      if (applyChanges)
375      {
376        this.configuration = configuration;
377        this.defaultStorageSchemes = defaultStorageSchemes;
378        this.deprecatedStorageSchemes = deprecatedStorageSchemes;
379        this.notificationHandlers = notificationHandlers;
380        this.passwordGenerator = passwordGenerator;
381        this.passwordValidators = passwordValidators;
382        this.requireChangeByTime = requireChangeByTime;
383      }
384    }
385
386    @Override
387    public boolean isAuthPasswordSyntax()
388    {
389      return authPasswordSyntax;
390    }
391
392    @Override
393    public List<PasswordStorageScheme<?>> getDefaultPasswordStorageSchemes()
394    {
395      return defaultStorageSchemes;
396    }
397
398    @Override
399    public Set<String> getDeprecatedPasswordStorageSchemes()
400    {
401      return deprecatedStorageSchemes;
402    }
403
404    @Override
405    public DN getDN()
406    {
407      return configuration.dn();
408    }
409
410    @Override
411    public boolean isDefaultPasswordStorageScheme(String name)
412    {
413      for (PasswordStorageScheme<?> s : defaultStorageSchemes)
414      {
415        String schemeName = authPasswordSyntax
416            ? s.getAuthPasswordSchemeName()
417            : s.getStorageSchemeName();
418        if (schemeName.equalsIgnoreCase(name))
419        {
420          return true;
421        }
422      }
423
424      return false;
425    }
426
427    @Override
428    public boolean isDeprecatedPasswordStorageScheme(String name)
429    {
430      return deprecatedStorageSchemes.contains(toLowerCase(name));
431    }
432
433    @Override
434    public Collection<PasswordValidator<?>> getPasswordValidators()
435    {
436      return passwordValidators.values();
437    }
438
439    @Override
440    public Collection<AccountStatusNotificationHandler<?>>
441      getAccountStatusNotificationHandlers()
442    {
443      return notificationHandlers.values();
444    }
445
446    @Override
447    public PasswordGenerator<?> getPasswordGenerator()
448    {
449      return passwordGenerator;
450    }
451
452    @Override
453    public long getRequireChangeByTime()
454    {
455      return requireChangeByTime;
456    }
457
458    /**
459     * Retrieves a string representation of this password policy.
460     *
461     * @return A string representation of this password policy.
462     */
463    @Override
464    public String toString()
465    {
466      StringBuilder buffer = new StringBuilder();
467      toString(buffer);
468      return buffer.toString();
469    }
470
471    /**
472     * Appends a string representation of this password policy to the provided
473     * buffer.
474     *
475     * @param buffer
476     *          The buffer to which the information should be appended.
477     */
478    public void toString(StringBuilder buffer)
479    {
480      buffer.append("Password Attribute:                    ");
481      buffer.append(configuration.getPasswordAttribute().getNameOrOID());
482      buffer.append(EOL);
483
484      buffer.append("Default Password Storage Schemes:      ");
485      if (defaultStorageSchemes == null || defaultStorageSchemes.isEmpty())
486      {
487        buffer.append("{none specified}");
488        buffer.append(EOL);
489      }
490      else
491      {
492        Iterator<PasswordStorageScheme<?>> iterator = defaultStorageSchemes
493            .iterator();
494        buffer.append(iterator.next().getStorageSchemeName());
495        buffer.append(EOL);
496
497        while (iterator.hasNext())
498        {
499          buffer.append("                                       ");
500          buffer.append(iterator.next().getStorageSchemeName());
501          buffer.append(EOL);
502        }
503      }
504
505      buffer.append("Deprecated Password Storage Schemes:   ");
506      if (deprecatedStorageSchemes == null || deprecatedStorageSchemes.isEmpty())
507      {
508        buffer.append("{none specified}");
509        buffer.append(EOL);
510      }
511      else
512      {
513        Iterator<String> iterator = deprecatedStorageSchemes.iterator();
514        buffer.append(iterator.next());
515        buffer.append(EOL);
516
517        while (iterator.hasNext())
518        {
519          buffer.append("                                       ");
520          buffer.append(iterator.next());
521          buffer.append(EOL);
522        }
523      }
524
525      buffer.append("Allow Multiple Password Values:        ");
526      buffer.append(configuration.isAllowMultiplePasswordValues());
527      buffer.append(EOL);
528
529      buffer.append("Allow Pre-Encoded Passwords:           ");
530      buffer.append(configuration.isAllowPreEncodedPasswords());
531      buffer.append(EOL);
532
533      buffer.append("Allow User Password Changes:           ");
534      buffer.append(configuration.isAllowUserPasswordChanges());
535      buffer.append(EOL);
536
537      buffer.append("Force Password Change on Add:          ");
538      buffer.append(configuration.isForceChangeOnAdd());
539      buffer.append(EOL);
540
541      buffer.append("Force Password Change on Admin Reset:  ");
542      buffer.append(configuration.isForceChangeOnReset());
543      buffer.append(EOL);
544
545      buffer.append("Require Current Password:              ");
546      buffer.append(configuration.isPasswordChangeRequiresCurrentPassword());
547      buffer.append(EOL);
548
549      buffer.append("Require Secure Authentication:         ");
550      buffer.append(configuration.isRequireSecureAuthentication());
551      buffer.append(EOL);
552
553      buffer.append("Require Secure Password Changes:       ");
554      buffer.append(configuration.isRequireSecurePasswordChanges());
555      buffer.append(EOL);
556
557      buffer.append("Lockout Failure Expiration Interval:   ");
558      buffer.append(configuration.getLockoutFailureExpirationInterval());
559      buffer.append(" seconds");
560      buffer.append(EOL);
561
562      buffer.append("Password Validators:                   ");
563      if (passwordValidators == null || passwordValidators.isEmpty())
564      {
565        buffer.append("{none specified}");
566        buffer.append(EOL);
567      }
568      else
569      {
570        Iterator<DN> iterator = passwordValidators.keySet().iterator();
571        buffer.append(iterator.next());
572        buffer.append(EOL);
573
574        while (iterator.hasNext())
575        {
576          buffer.append("                                       ");
577          buffer.append(iterator.next());
578          buffer.append(EOL);
579        }
580      }
581
582      buffer.append("Skip Validation for Administrators:    ");
583      buffer.append(configuration.isSkipValidationForAdministrators());
584      buffer.append(EOL);
585
586      buffer.append("Password Generator:                    ");
587      if (passwordGenerator == null)
588      {
589        buffer.append("{none specified}");
590      }
591      else
592      {
593        buffer.append(configuration.getPasswordGeneratorDN());
594      }
595      buffer.append(EOL);
596
597      buffer.append("Account Status Notification Handlers:  ");
598      if (notificationHandlers == null || notificationHandlers.isEmpty())
599      {
600        buffer.append("{none specified}");
601        buffer.append(EOL);
602      }
603      else
604      {
605        Iterator<DN> iterator = notificationHandlers.keySet().iterator();
606        buffer.append(iterator.next());
607        buffer.append(EOL);
608
609        while (iterator.hasNext())
610        {
611          buffer.append("                                       ");
612          buffer.append(iterator.next());
613          buffer.append(EOL);
614        }
615      }
616
617      buffer.append("Minimum Password Age:                  ");
618      buffer.append(configuration.getMinPasswordAge());
619      buffer.append(" seconds");
620      buffer.append(EOL);
621
622      buffer.append("Maximum Password Age:                  ");
623      buffer.append(configuration.getMaxPasswordAge());
624      buffer.append(" seconds");
625      buffer.append(EOL);
626
627      buffer.append("Maximum Password Reset Age:            ");
628      buffer.append(configuration.getMaxPasswordResetAge());
629      buffer.append(" seconds");
630      buffer.append(EOL);
631
632      buffer.append("Expiration Warning Interval:           ");
633      buffer.append(configuration.getPasswordExpirationWarningInterval());
634      buffer.append(" seconds");
635      buffer.append(EOL);
636
637      buffer.append("Expire Passwords Without Warning:      ");
638      buffer.append(configuration.isExpirePasswordsWithoutWarning());
639      buffer.append(EOL);
640
641      buffer.append("Allow Expired Password Changes:        ");
642      buffer.append(configuration.isAllowExpiredPasswordChanges());
643      buffer.append(EOL);
644
645      buffer.append("Grace Login Count:                     ");
646      buffer.append(configuration.getGraceLoginCount());
647      buffer.append(EOL);
648
649      buffer.append("Lockout Failure Count:                 ");
650      buffer.append(configuration.getLockoutFailureCount());
651      buffer.append(EOL);
652
653      buffer.append("Lockout Duration:                      ");
654      buffer.append(configuration.getLockoutDuration());
655      buffer.append(" seconds");
656      buffer.append(EOL);
657
658      buffer.append("Lockout Count Expiration Interval:     ");
659      buffer.append(configuration.getLockoutFailureExpirationInterval());
660      buffer.append(" seconds");
661      buffer.append(EOL);
662
663      buffer.append("Required Password Change By Time:      ");
664      if (requireChangeByTime <= 0)
665      {
666        buffer.append("{none specified}");
667      }
668      else
669      {
670        SimpleDateFormat dateFormat = new SimpleDateFormat(
671            DATE_FORMAT_GENERALIZED_TIME);
672        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
673        buffer.append(dateFormat.format(new Date(requireChangeByTime)));
674      }
675      buffer.append(EOL);
676
677      buffer.append("Last Login Time Attribute:             ");
678      if (configuration.getLastLoginTimeAttribute() != null)
679      {
680        buffer.append(configuration.getLastLoginTimeAttribute().getNameOrOID());
681      }
682      else
683      {
684        buffer.append("{none specified}");
685      }
686      buffer.append(EOL);
687
688      buffer.append("Last Login Time Format:                ");
689      if (configuration.getLastLoginTimeFormat() != null)
690      {
691        buffer.append(configuration.getLastLoginTimeFormat());
692      }
693      else
694      {
695        buffer.append("{none specified}");
696      }
697      buffer.append(EOL);
698
699      buffer.append("Previous Last Login Time Formats:      ");
700      if (configuration.getPreviousLastLoginTimeFormat().isEmpty())
701      {
702        buffer.append("{none specified}");
703        buffer.append(EOL);
704      }
705      else
706      {
707        Iterator<String> iterator = configuration
708            .getPreviousLastLoginTimeFormat().iterator();
709
710        buffer.append(iterator.next());
711        buffer.append(EOL);
712
713        while (iterator.hasNext())
714        {
715          buffer.append("                                       ");
716          buffer.append(iterator.next());
717          buffer.append(EOL);
718        }
719      }
720
721      buffer.append("Idle Lockout Interval:                 ");
722      buffer.append(configuration.getIdleLockoutInterval());
723      buffer.append(" seconds");
724      buffer.append(EOL);
725
726      buffer.append("History Count:                         ");
727      buffer.append(configuration.getPasswordHistoryCount());
728      buffer.append(EOL);
729
730      buffer.append("Update Failure Policy:                 ");
731      buffer.append(configuration.getStateUpdateFailurePolicy());
732      buffer.append(EOL);
733    }
734
735    @Override
736    public boolean isAllowExpiredPasswordChanges()
737    {
738      return configuration.isAllowExpiredPasswordChanges();
739    }
740
741    @Override
742    public boolean isAllowMultiplePasswordValues()
743    {
744      return configuration.isAllowMultiplePasswordValues();
745    }
746
747    @Override
748    public boolean isAllowPreEncodedPasswords()
749    {
750      return configuration.isAllowPreEncodedPasswords();
751    }
752
753    @Override
754    public boolean isAllowUserPasswordChanges()
755    {
756      return configuration.isAllowUserPasswordChanges();
757    }
758
759    @Override
760    public boolean isExpirePasswordsWithoutWarning()
761    {
762      return configuration.isExpirePasswordsWithoutWarning();
763    }
764
765    @Override
766    public boolean isForceChangeOnAdd()
767    {
768      return configuration.isForceChangeOnAdd();
769    }
770
771    @Override
772    public boolean isForceChangeOnReset()
773    {
774      return configuration.isForceChangeOnReset();
775    }
776
777    @Override
778    public int getGraceLoginCount()
779    {
780      return configuration.getGraceLoginCount();
781    }
782
783    @Override
784    public long getIdleLockoutInterval()
785    {
786      return configuration.getIdleLockoutInterval();
787    }
788
789    @Override
790    public AttributeType getLastLoginTimeAttribute()
791    {
792      return configuration.getLastLoginTimeAttribute();
793    }
794
795    @Override
796    public String getLastLoginTimeFormat()
797    {
798      return configuration.getLastLoginTimeFormat();
799    }
800
801    @Override
802    public long getLockoutDuration()
803    {
804      return configuration.getLockoutDuration();
805    }
806
807    @Override
808    public int getLockoutFailureCount()
809    {
810      return configuration.getLockoutFailureCount();
811    }
812
813    @Override
814    public long getLockoutFailureExpirationInterval()
815    {
816      return configuration.getLockoutFailureExpirationInterval();
817    }
818
819    @Override
820    public long getMaxPasswordAge()
821    {
822      return configuration.getMaxPasswordAge();
823    }
824
825    @Override
826    public long getMaxPasswordResetAge()
827    {
828      return configuration.getMaxPasswordResetAge();
829    }
830
831    @Override
832    public long getMinPasswordAge()
833    {
834      return configuration.getMinPasswordAge();
835    }
836
837    @Override
838    public AttributeType getPasswordAttribute()
839    {
840      return configuration.getPasswordAttribute();
841    }
842
843    @Override
844    public boolean isPasswordChangeRequiresCurrentPassword()
845    {
846      return configuration.isPasswordChangeRequiresCurrentPassword();
847    }
848
849    @Override
850    public long getPasswordExpirationWarningInterval()
851    {
852      return configuration.getPasswordExpirationWarningInterval();
853    }
854
855    @Override
856    public int getPasswordHistoryCount()
857    {
858      return configuration.getPasswordHistoryCount();
859    }
860
861    @Override
862    public long getPasswordHistoryDuration()
863    {
864      return configuration.getPasswordHistoryDuration();
865    }
866
867    @Override
868    public SortedSet<String> getPreviousLastLoginTimeFormats()
869    {
870      return configuration.getPreviousLastLoginTimeFormat();
871    }
872
873    @Override
874    public boolean isRequireSecureAuthentication()
875    {
876      return configuration.isRequireSecureAuthentication();
877    }
878
879    @Override
880    public boolean isRequireSecurePasswordChanges()
881    {
882      return configuration.isRequireSecurePasswordChanges();
883    }
884
885    @Override
886    public boolean isSkipValidationForAdministrators()
887    {
888      return configuration.isSkipValidationForAdministrators();
889    }
890
891    @Override
892    public StateUpdateFailurePolicy getStateUpdateFailurePolicy()
893    {
894      return configuration.getStateUpdateFailurePolicy();
895    }
896  }
897
898  private ServerContext serverContext;
899
900  /** Default constructor instantiated from authentication policy config manager. */
901  public PasswordPolicyFactory()
902  {
903    // Nothing to do .
904  }
905
906  /**
907   * Sets the server context.
908   *
909   * @param serverContext
910   *            The server context.
911   */
912  @Override
913  public void setServerContext(final ServerContext serverContext) {
914    this.serverContext = serverContext;
915  }
916
917  @Override
918  public PasswordPolicy createAuthenticationPolicy(
919      final PasswordPolicyCfg configuration) throws ConfigException,
920      InitializationException
921  {
922    PasswordPolicyImpl policy = new PasswordPolicyImpl(serverContext, configuration);
923    configuration.addPasswordPolicyChangeListener(policy);
924    return policy;
925  }
926
927  @Override
928  public boolean isConfigurationAcceptable(
929      final PasswordPolicyCfg configuration,
930      final List<LocalizableMessage> unacceptableReasons)
931  {
932    try
933    {
934      new PasswordPolicyImpl(null, configuration);
935    }
936    catch (final ConfigException | InitializationException ie)
937    {
938      logger.traceException(ie);
939
940      unacceptableReasons.add(ie.getMessageObject());
941      return false;
942    }
943
944    // If we made it here, then the configuration is acceptable.
945    return true;
946  }
947}