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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.extensions; 028 import org.opends.messages.Message; 029 030 031 032 import java.util.ArrayList; 033 import java.util.List; 034 import java.util.Set; 035 036 import org.opends.server.admin.server.ConfigurationChangeListener; 037 import org.opends.server.admin.std.server. 038 RepeatedCharactersPasswordValidatorCfg; 039 import org.opends.server.api.PasswordValidator; 040 import org.opends.server.types.ConfigChangeResult; 041 import org.opends.server.types.ByteString; 042 import org.opends.server.types.Entry; 043 import org.opends.server.types.Operation; 044 import org.opends.server.types.ResultCode; 045 046 import static org.opends.messages.ExtensionMessages.*; 047 import org.opends.messages.MessageBuilder; 048 049 050 /** 051 * This class provides an OpenDS password validator that may be used to ensure 052 * that proposed passwords are not allowed to have the same character appear 053 * several times consecutively. 054 */ 055 public class RepeatedCharactersPasswordValidator 056 extends PasswordValidator<RepeatedCharactersPasswordValidatorCfg> 057 implements ConfigurationChangeListener< 058 RepeatedCharactersPasswordValidatorCfg> 059 { 060 // The current configuration for this password validator. 061 private RepeatedCharactersPasswordValidatorCfg currentConfig; 062 063 064 065 /** 066 * Creates a new instance of this repeated characters password validator. 067 */ 068 public RepeatedCharactersPasswordValidator() 069 { 070 super(); 071 072 // No implementation is required here. All initialization should be 073 // performed in the initializePasswordValidator() method. 074 } 075 076 077 078 /** 079 * {@inheritDoc} 080 */ 081 @Override() 082 public void initializePasswordValidator( 083 RepeatedCharactersPasswordValidatorCfg configuration) 084 { 085 configuration.addRepeatedCharactersChangeListener(this); 086 currentConfig = configuration; 087 } 088 089 090 091 /** 092 * {@inheritDoc} 093 */ 094 @Override() 095 public void finalizePasswordValidator() 096 { 097 currentConfig.removeRepeatedCharactersChangeListener(this); 098 } 099 100 101 102 /** 103 * {@inheritDoc} 104 */ 105 @Override() 106 public boolean passwordIsAcceptable(ByteString newPassword, 107 Set<ByteString> currentPasswords, 108 Operation operation, Entry userEntry, 109 MessageBuilder invalidReason) 110 { 111 // Get a handle to the current configuration and see if we need to count 112 // the number of repeated characters in the password. 113 RepeatedCharactersPasswordValidatorCfg config = currentConfig; 114 int maxRepeats = config.getMaxConsecutiveLength(); 115 if (maxRepeats <= 0) 116 { 117 // We don't need to check anything, so the password will be acceptable. 118 return true; 119 } 120 121 122 // Get the password as a string. If we should use case-insensitive 123 // validation, then convert it to use all lowercase characters. 124 String passwordString = newPassword.stringValue(); 125 if (! config.isCaseSensitiveValidation()) 126 { 127 passwordString = passwordString.toLowerCase(); 128 } 129 130 131 // Create variables to keep track of the last character we've seen and how 132 // many times we have seen it. 133 char lastCharacter = '\u0000'; 134 int consecutiveCount = 0; 135 136 137 // Iterate through the characters in the password. If the consecutive 138 // count ever gets too high, then fail. 139 for (int i=0; i < passwordString.length(); i++) 140 { 141 char currentCharacter = passwordString.charAt(i); 142 if (currentCharacter == lastCharacter) 143 { 144 consecutiveCount++; 145 if (consecutiveCount > maxRepeats) 146 { 147 Message message = 148 ERR_REPEATEDCHARS_VALIDATOR_TOO_MANY_CONSECUTIVE.get( 149 maxRepeats); 150 invalidReason.append(message); 151 return false; 152 } 153 } 154 else 155 { 156 lastCharacter = currentCharacter; 157 consecutiveCount = 1; 158 } 159 } 160 161 return true; 162 } 163 164 165 166 /** 167 * {@inheritDoc} 168 */ 169 public boolean isConfigurationChangeAcceptable( 170 RepeatedCharactersPasswordValidatorCfg configuration, 171 List<Message> unacceptableReasons) 172 { 173 // All of the necessary validation should have been performed automatically, 174 // so if we get to this point then the new configuration will be acceptable. 175 return true; 176 } 177 178 179 180 /** 181 * {@inheritDoc} 182 */ 183 public ConfigChangeResult applyConfigurationChange( 184 RepeatedCharactersPasswordValidatorCfg configuration) 185 { 186 ResultCode resultCode = ResultCode.SUCCESS; 187 boolean adminActionRequired = false; 188 ArrayList<Message> messages = new ArrayList<Message>(); 189 190 // For this password validator, we will always be able to successfully apply 191 // the new configuration. 192 currentConfig = configuration; 193 194 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 195 } 196 } 197