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