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-2016 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.forgerock.opendj.config.server.ConfigurationChangeListener; 030import org.forgerock.opendj.server.config.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 /** Creates a new instance of this repeated characters password validator. */ 049 public RepeatedCharactersPasswordValidator() 050 { 051 super(); 052 053 // No implementation is required here. All initialization should be 054 // performed in the initializePasswordValidator() method. 055 } 056 057 @Override 058 public void initializePasswordValidator( 059 RepeatedCharactersPasswordValidatorCfg configuration) 060 { 061 configuration.addRepeatedCharactersChangeListener(this); 062 currentConfig = configuration; 063 } 064 065 @Override 066 public void finalizePasswordValidator() 067 { 068 currentConfig.removeRepeatedCharactersChangeListener(this); 069 } 070 071 @Override 072 public boolean passwordIsAcceptable(ByteString newPassword, 073 Set<ByteString> currentPasswords, 074 Operation operation, Entry userEntry, 075 LocalizableMessageBuilder invalidReason) 076 { 077 // Get a handle to the current configuration and see if we need to count 078 // the number of repeated characters in the password. 079 RepeatedCharactersPasswordValidatorCfg config = currentConfig; 080 int maxRepeats = config.getMaxConsecutiveLength(); 081 if (maxRepeats <= 0) 082 { 083 // We don't need to check anything, so the password will be acceptable. 084 return true; 085 } 086 087 // Get the password as a string. If we should use case-insensitive 088 // validation, then convert it to use all lowercase characters. 089 String passwordString = newPassword.toString(); 090 if (! config.isCaseSensitiveValidation()) 091 { 092 passwordString = passwordString.toLowerCase(); 093 } 094 095 // Create variables to keep track of the last character we've seen and how 096 // many times we have seen it. 097 char lastCharacter = '\u0000'; 098 int consecutiveCount = 0; 099 100 // Iterate through the characters in the password. If the consecutive 101 // count ever gets too high, then fail. 102 for (int i=0; i < passwordString.length(); i++) 103 { 104 char currentCharacter = passwordString.charAt(i); 105 if (currentCharacter == lastCharacter) 106 { 107 consecutiveCount++; 108 if (consecutiveCount > maxRepeats) 109 { 110 LocalizableMessage message = 111 ERR_REPEATEDCHARS_VALIDATOR_TOO_MANY_CONSECUTIVE.get( 112 maxRepeats); 113 invalidReason.append(message); 114 return false; 115 } 116 } 117 else 118 { 119 lastCharacter = currentCharacter; 120 consecutiveCount = 1; 121 } 122 } 123 124 return true; 125 } 126 127 @Override 128 public boolean isConfigurationChangeAcceptable( 129 RepeatedCharactersPasswordValidatorCfg configuration, 130 List<LocalizableMessage> unacceptableReasons) 131 { 132 // All of the necessary validation should have been performed automatically, 133 // so if we get to this point then the new configuration will be acceptable. 134 return true; 135 } 136 137 @Override 138 public ConfigChangeResult applyConfigurationChange( 139 RepeatedCharactersPasswordValidatorCfg configuration) 140 { 141 // For this password validator, we will always be able to successfully apply 142 // the new configuration. 143 currentConfig = configuration; 144 return new ConfigChangeResult(); 145 } 146}