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.i18n.LocalizableMessage; 025import org.forgerock.i18n.LocalizableMessageBuilder; 026import org.forgerock.opendj.config.server.ConfigChangeResult; 027import org.forgerock.opendj.config.server.ConfigException; 028import org.forgerock.opendj.ldap.ByteString; 029import org.opends.server.api.PasswordValidator; 030import org.opends.server.types.*; 031import org.opends.server.util.LevenshteinDistance; 032import org.forgerock.opendj.server.config.server.SimilarityBasedPasswordValidatorCfg; 033import org.forgerock.opendj.config.server.ConfigurationChangeListener; 034 035/** 036 * This class provides a password validator that can ensure that the provided 037 * password meets minimum similarity requirements. 038 */ 039public class SimilarityBasedPasswordValidator extends 040 PasswordValidator<SimilarityBasedPasswordValidatorCfg> implements 041 ConfigurationChangeListener<SimilarityBasedPasswordValidatorCfg> 042{ 043 /** The current configuration for this password validator. */ 044 private SimilarityBasedPasswordValidatorCfg currentConfig; 045 046 /** Creates a new instance of this password validator. */ 047 public SimilarityBasedPasswordValidator() 048 { 049 super(); 050 051 // All initialization must be done in the initializePasswordValidator 052 // method. 053 } 054 055 @Override 056 public void initializePasswordValidator( 057 SimilarityBasedPasswordValidatorCfg configuration) 058 throws ConfigException, InitializationException 059 { 060 configuration.addSimilarityBasedChangeListener(this); 061 062 currentConfig = configuration; 063 } 064 065 @Override 066 public void finalizePasswordValidator() 067 { 068 currentConfig.removeSimilarityBasedChangeListener(this); 069 } 070 071 @Override 072 public boolean passwordIsAcceptable(ByteString newPassword, 073 Set<ByteString> currentPasswords, 074 Operation operation, Entry userEntry, 075 LocalizableMessageBuilder invalidReason) { 076 int minDifference = currentConfig.getMinPasswordDifference(); 077 ByteString passwd = newPassword == null 078 ? ByteString.empty() 079 : newPassword; 080 081 if (currentPasswords == null || currentPasswords.isEmpty()) { 082 // This validator requires access to at least one current password. 083 // If we don't have a current password, then we can't validate it, so 084 // we'll have to assume it is OK. Ideally, the password policy should be 085 // configured to always require the current password, but even then the 086 // current password probably won't be available during an administrative 087 // password reset. 088 return true; 089 } 090 091 for (ByteString bs : currentPasswords) { 092 if (bs == null) { 093 continue; 094 } 095 int ldistance = LevenshteinDistance.calculate(passwd.toString(), 096 bs.toString()); 097 if (ldistance < minDifference) { 098 invalidReason.append(ERR_PWDIFFERENCEVALIDATOR_TOO_SMALL.get( 099 minDifference)); 100 return false; 101 } 102 } 103 104 return true; 105 } 106 107 @Override 108 public boolean isConfigurationChangeAcceptable( 109 SimilarityBasedPasswordValidatorCfg configuration, 110 List<LocalizableMessage> unacceptableReasons) 111 { 112 return true; 113 } 114 115 @Override 116 public ConfigChangeResult applyConfigurationChange( 117 SimilarityBasedPasswordValidatorCfg configuration) 118 { 119 currentConfig = configuration; 120 return new ConfigChangeResult(); 121 } 122}