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 2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2015 ForgeRock AS. 016 */ 017package org.opends.server.extensions; 018 019import static org.opends.messages.ExtensionMessages.*; 020 021import java.util.List; 022import java.util.Set; 023 024import org.forgerock.opendj.config.server.ConfigChangeResult; 025import org.forgerock.opendj.ldap.ByteString; 026import org.forgerock.i18n.LocalizableMessageBuilder; 027import org.forgerock.i18n.LocalizableMessage; 028 029import org.opends.server.admin.server.ConfigurationChangeListener; 030import org.opends.server.admin.std.server. 031 RepeatedCharactersPasswordValidatorCfg; 032import org.opends.server.api.PasswordValidator; 033import org.opends.server.types.*; 034 035/** 036 * This class provides an OpenDS password validator that may be used to ensure 037 * that proposed passwords are not allowed to have the same character appear 038 * several times consecutively. 039 */ 040public class RepeatedCharactersPasswordValidator 041 extends PasswordValidator<RepeatedCharactersPasswordValidatorCfg> 042 implements ConfigurationChangeListener< 043 RepeatedCharactersPasswordValidatorCfg> 044{ 045 /** The current configuration for this password validator. */ 046 private RepeatedCharactersPasswordValidatorCfg currentConfig; 047 048 049 050 /** 051 * Creates a new instance of this repeated characters password validator. 052 */ 053 public RepeatedCharactersPasswordValidator() 054 { 055 super(); 056 057 // No implementation is required here. All initialization should be 058 // performed in the initializePasswordValidator() method. 059 } 060 061 062 063 /** {@inheritDoc} */ 064 @Override 065 public void initializePasswordValidator( 066 RepeatedCharactersPasswordValidatorCfg configuration) 067 { 068 configuration.addRepeatedCharactersChangeListener(this); 069 currentConfig = configuration; 070 } 071 072 073 074 /** {@inheritDoc} */ 075 @Override 076 public void finalizePasswordValidator() 077 { 078 currentConfig.removeRepeatedCharactersChangeListener(this); 079 } 080 081 082 083 /** {@inheritDoc} */ 084 @Override 085 public boolean passwordIsAcceptable(ByteString newPassword, 086 Set<ByteString> currentPasswords, 087 Operation operation, Entry userEntry, 088 LocalizableMessageBuilder invalidReason) 089 { 090 // Get a handle to the current configuration and see if we need to count 091 // the number of repeated characters in the password. 092 RepeatedCharactersPasswordValidatorCfg config = currentConfig; 093 int maxRepeats = config.getMaxConsecutiveLength(); 094 if (maxRepeats <= 0) 095 { 096 // We don't need to check anything, so the password will be acceptable. 097 return true; 098 } 099 100 101 // Get the password as a string. If we should use case-insensitive 102 // validation, then convert it to use all lowercase characters. 103 String passwordString = newPassword.toString(); 104 if (! config.isCaseSensitiveValidation()) 105 { 106 passwordString = passwordString.toLowerCase(); 107 } 108 109 110 // Create variables to keep track of the last character we've seen and how 111 // many times we have seen it. 112 char lastCharacter = '\u0000'; 113 int consecutiveCount = 0; 114 115 116 // Iterate through the characters in the password. If the consecutive 117 // count ever gets too high, then fail. 118 for (int i=0; i < passwordString.length(); i++) 119 { 120 char currentCharacter = passwordString.charAt(i); 121 if (currentCharacter == lastCharacter) 122 { 123 consecutiveCount++; 124 if (consecutiveCount > maxRepeats) 125 { 126 LocalizableMessage message = 127 ERR_REPEATEDCHARS_VALIDATOR_TOO_MANY_CONSECUTIVE.get( 128 maxRepeats); 129 invalidReason.append(message); 130 return false; 131 } 132 } 133 else 134 { 135 lastCharacter = currentCharacter; 136 consecutiveCount = 1; 137 } 138 } 139 140 return true; 141 } 142 143 144 145 /** {@inheritDoc} */ 146 public boolean isConfigurationChangeAcceptable( 147 RepeatedCharactersPasswordValidatorCfg configuration, 148 List<LocalizableMessage> unacceptableReasons) 149 { 150 // All of the necessary validation should have been performed automatically, 151 // so if we get to this point then the new configuration will be acceptable. 152 return true; 153 } 154 155 156 157 /** {@inheritDoc} */ 158 public ConfigChangeResult applyConfigurationChange( 159 RepeatedCharactersPasswordValidatorCfg configuration) 160 { 161 // For this password validator, we will always be able to successfully apply 162 // the new configuration. 163 currentConfig = configuration; 164 return new ConfigChangeResult(); 165 } 166}