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