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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.extensions;
028
029import org.forgerock.i18n.LocalizableMessage;
030import java.util.List;
031import java.util.Set;
032
033import org.opends.server.admin.server.ConfigurationChangeListener;
034import org.opends.server.admin.std.server.LengthBasedPasswordValidatorCfg;
035import org.opends.server.admin.std.server.PasswordValidatorCfg;
036import org.opends.server.api.PasswordValidator;
037import org.forgerock.opendj.config.server.ConfigChangeResult;
038import org.forgerock.opendj.config.server.ConfigException;
039import org.opends.server.types.*;
040import org.forgerock.opendj.ldap.ByteString;
041import static org.opends.messages.ExtensionMessages.*;
042import org.forgerock.i18n.LocalizableMessageBuilder;
043
044/**
045 * This class provides a password validator that can ensure that the provided
046 * password meets minimum and/or maximum length requirements.
047 */
048public class LengthBasedPasswordValidator extends
049    PasswordValidator<LengthBasedPasswordValidatorCfg> implements
050    ConfigurationChangeListener<LengthBasedPasswordValidatorCfg>
051{
052  /** The current configuration for this password validator. */
053  private LengthBasedPasswordValidatorCfg currentConfig;
054
055
056
057  /**
058   * Creates a new instance of this password validator.
059   */
060  public LengthBasedPasswordValidator()
061  {
062    super();
063
064    // All initialization must be done in the initializePasswordValidator
065    // method.
066  }
067
068
069
070  /** {@inheritDoc} */
071  @Override
072  public void initializePasswordValidator(
073                   LengthBasedPasswordValidatorCfg configuration)
074         throws ConfigException, InitializationException
075  {
076    configuration.addLengthBasedChangeListener(this);
077
078    currentConfig = configuration;
079
080    // Make sure that if both the maximum and minimum lengths are set, the
081    // maximum length is greater than or equal to the minimum length.
082    int maxLength = configuration.getMaxPasswordLength();
083    int minLength = configuration.getMinPasswordLength();
084    if (maxLength > 0 && minLength > 0 && minLength > maxLength)
085    {
086      LocalizableMessage message =
087          ERR_PWLENGTHVALIDATOR_MIN_GREATER_THAN_MAX.get(minLength, maxLength);
088      throw new ConfigException(message);
089    }
090  }
091
092
093
094  /** {@inheritDoc} */
095  @Override
096  public void finalizePasswordValidator()
097  {
098    currentConfig.removeLengthBasedChangeListener(this);
099  }
100
101
102
103  /** {@inheritDoc} */
104  @Override
105  public boolean passwordIsAcceptable(ByteString newPassword,
106                                      Set<ByteString> currentPasswords,
107                                      Operation operation, Entry userEntry,
108                                      LocalizableMessageBuilder invalidReason)
109  {
110    LengthBasedPasswordValidatorCfg config = currentConfig;
111
112    int numChars = newPassword.toString().length();
113
114    int minLength = config.getMinPasswordLength();
115    if (minLength > 0 && numChars < minLength)
116    {
117      invalidReason.append(ERR_PWLENGTHVALIDATOR_TOO_SHORT.get(minLength));
118      return false;
119    }
120
121    int maxLength = config.getMaxPasswordLength();
122    if (maxLength > 0 && numChars > maxLength)
123    {
124      invalidReason.append(ERR_PWLENGTHVALIDATOR_TOO_LONG.get(maxLength));
125      return false;
126    }
127
128    return true;
129  }
130
131
132
133  /** {@inheritDoc} */
134  @Override
135  public boolean isConfigurationAcceptable(PasswordValidatorCfg configuration,
136                                           List<LocalizableMessage> unacceptableReasons)
137  {
138    LengthBasedPasswordValidatorCfg config =
139         (LengthBasedPasswordValidatorCfg) configuration;
140    return isConfigurationChangeAcceptable(config, unacceptableReasons);
141  }
142
143
144
145  /** {@inheritDoc} */
146  public boolean isConfigurationChangeAcceptable(
147                      LengthBasedPasswordValidatorCfg configuration,
148                      List<LocalizableMessage> unacceptableReasons)
149  {
150    // Make sure that if both the maximum and minimum lengths are set, the
151    // maximum length is greater than or equal to the minimum length.
152    int maxLength = configuration.getMaxPasswordLength();
153    int minLength = configuration.getMinPasswordLength();
154    if (maxLength > 0 && minLength > 0 && minLength > maxLength)
155    {
156      LocalizableMessage message = ERR_PWLENGTHVALIDATOR_MIN_GREATER_THAN_MAX.get(
157              minLength, maxLength);
158      unacceptableReasons.add(message);
159      return false;
160    }
161
162    return true;
163  }
164
165
166
167  /** {@inheritDoc} */
168  public ConfigChangeResult applyConfigurationChange(
169              LengthBasedPasswordValidatorCfg configuration)
170  {
171    currentConfig = configuration;
172    return new ConfigChangeResult();
173  }
174}