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.AttributeValuePasswordValidatorCfg; 038 import org.opends.server.admin.std.server.PasswordValidatorCfg; 039 import org.opends.server.api.PasswordValidator; 040 import org.opends.server.types.Attribute; 041 import org.opends.server.types.AttributeType; 042 import org.opends.server.types.AttributeValue; 043 import org.opends.server.types.ConfigChangeResult; 044 import org.opends.server.types.ByteString; 045 import org.opends.server.types.Entry; 046 import org.opends.server.types.Operation; 047 import org.opends.server.types.ResultCode; 048 049 import static org.opends.messages.ExtensionMessages.*; 050 import org.opends.messages.MessageBuilder; 051 052 053 /** 054 * This class provides an OpenDS password validator that may be used to ensure 055 * that proposed passwords are not contained in another attribute in the user's 056 * entry. 057 */ 058 public class AttributeValuePasswordValidator 059 extends PasswordValidator<AttributeValuePasswordValidatorCfg> 060 implements ConfigurationChangeListener< 061 AttributeValuePasswordValidatorCfg> 062 { 063 // The current configuration for this password validator. 064 private AttributeValuePasswordValidatorCfg currentConfig; 065 066 067 068 /** 069 * Creates a new instance of this attribute value password validator. 070 */ 071 public AttributeValuePasswordValidator() 072 { 073 super(); 074 075 // No implementation is required here. All initialization should be 076 // performed in the initializePasswordValidator() method. 077 } 078 079 080 081 /** 082 * {@inheritDoc} 083 */ 084 @Override() 085 public void initializePasswordValidator( 086 AttributeValuePasswordValidatorCfg configuration) 087 { 088 configuration.addAttributeValueChangeListener(this); 089 currentConfig = configuration; 090 } 091 092 093 094 /** 095 * {@inheritDoc} 096 */ 097 @Override() 098 public void finalizePasswordValidator() 099 { 100 currentConfig.removeAttributeValueChangeListener(this); 101 } 102 103 104 105 /** 106 * {@inheritDoc} 107 */ 108 @Override() 109 public boolean passwordIsAcceptable(ByteString newPassword, 110 Set<ByteString> currentPasswords, 111 Operation operation, Entry userEntry, 112 MessageBuilder invalidReason) 113 { 114 // Get a handle to the current configuration. 115 AttributeValuePasswordValidatorCfg config = currentConfig; 116 117 118 // Get the string representation (both forward and reversed) for the 119 // password. 120 String password = newPassword.stringValue(); 121 String reversed = new StringBuilder(password).reverse().toString(); 122 123 124 // If we should check a specific set of attributes, then do that now. 125 // Otherwise, check all user attributes. 126 Set<AttributeType> matchAttributes = config.getMatchAttribute(); 127 if ((matchAttributes == null) || matchAttributes.isEmpty()) 128 { 129 matchAttributes = userEntry.getUserAttributes().keySet(); 130 } 131 132 for (AttributeType t : matchAttributes) 133 { 134 List<Attribute> attrList = userEntry.getAttribute(t); 135 if ((attrList == null) || attrList.isEmpty()) 136 { 137 continue; 138 } 139 140 AttributeValue vf = new AttributeValue(t, password); 141 AttributeValue vr = new AttributeValue(t, reversed); 142 143 for (Attribute a : attrList) 144 { 145 if (a.hasValue(vf) || 146 (config.isTestReversedPassword() && a.hasValue(vr))) 147 { 148 149 invalidReason.append(ERR_ATTRVALUE_VALIDATOR_PASSWORD_IN_ENTRY.get()); 150 return false; 151 } 152 } 153 } 154 155 156 // If we've gotten here, then the password is acceptable. 157 return true; 158 } 159 160 161 162 /** 163 * {@inheritDoc} 164 */ 165 @Override() 166 public boolean isConfigurationAcceptable(PasswordValidatorCfg configuration, 167 List<Message> unacceptableReasons) 168 { 169 AttributeValuePasswordValidatorCfg config = 170 (AttributeValuePasswordValidatorCfg) configuration; 171 return isConfigurationChangeAcceptable(config, unacceptableReasons); 172 } 173 174 175 176 /** 177 * {@inheritDoc} 178 */ 179 public boolean isConfigurationChangeAcceptable( 180 AttributeValuePasswordValidatorCfg configuration, 181 List<Message> unacceptableReasons) 182 { 183 // If we've gotten this far, then we'll accept the change. 184 return true; 185 } 186 187 188 189 /** 190 * {@inheritDoc} 191 */ 192 public ConfigChangeResult applyConfigurationChange( 193 AttributeValuePasswordValidatorCfg configuration) 194 { 195 ResultCode resultCode = ResultCode.SUCCESS; 196 boolean adminActionRequired = false; 197 ArrayList<Message> messages = new ArrayList<Message>(); 198 199 200 currentConfig = configuration; 201 202 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 203 } 204 } 205