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 2006-2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.core; 018 019import static org.opends.messages.ConfigMessages.*; 020import static org.opends.server.util.StaticUtils.*; 021 022import java.util.ArrayList; 023import java.util.List; 024import java.util.concurrent.ConcurrentHashMap; 025 026import org.forgerock.i18n.LocalizableMessage; 027import org.forgerock.opendj.config.server.ConfigException; 028import org.forgerock.util.Utils; 029import org.forgerock.opendj.config.ClassPropertyDefinition; 030import org.forgerock.opendj.config.server.ConfigurationAddListener; 031import org.forgerock.opendj.config.server.ConfigurationChangeListener; 032import org.forgerock.opendj.config.server.ConfigurationDeleteListener; 033import org.forgerock.opendj.server.config.meta.PasswordStorageSchemeCfgDefn; 034import org.forgerock.opendj.server.config.server.PasswordStorageSchemeCfg; 035import org.forgerock.opendj.server.config.server.RootCfg; 036import org.opends.server.api.PasswordStorageScheme; 037import org.forgerock.opendj.config.server.ConfigChangeResult; 038import org.forgerock.opendj.ldap.DN; 039import org.opends.server.types.InitializationException; 040 041/** 042 * This class defines a utility that will be used to manage the set of password 043 * storage schemes defined in the Directory Server. It will initialize the 044 * storage schemes when the server starts, and then will manage any additions, 045 * removals, or modifications to any schemes while the server is running. 046 */ 047public class PasswordStorageSchemeConfigManager 048 implements 049 ConfigurationChangeListener <PasswordStorageSchemeCfg>, 050 ConfigurationAddListener <PasswordStorageSchemeCfg>, 051 ConfigurationDeleteListener <PasswordStorageSchemeCfg> 052{ 053 /** A mapping between the DNs of the config entries and the associated password storage schemes. */ 054 private final ConcurrentHashMap<DN,PasswordStorageScheme> storageSchemes; 055 056 private final ServerContext serverContext; 057 058 /** 059 * Creates a new instance of this password storage scheme config manager. 060 * 061 * @param serverContext 062 * The server context. 063 */ 064 public PasswordStorageSchemeConfigManager(ServerContext serverContext) 065 { 066 this.serverContext = serverContext; 067 storageSchemes = new ConcurrentHashMap<>(); 068 } 069 070 /** 071 * Initializes all password storage schemes currently defined in the Directory 072 * Server configuration. This should only be called at Directory Server 073 * startup. 074 * 075 * @throws ConfigException If a configuration problem causes the password 076 * storage scheme initialization process to fail. 077 * 078 * @throws InitializationException If a problem occurs while initializing 079 * the password storage scheme that is not 080 * related to the server configuration. 081 */ 082 public void initializePasswordStorageSchemes() 083 throws ConfigException, InitializationException 084 { 085 RootCfg rootConfiguration = serverContext.getRootConfig(); 086 rootConfiguration.addPasswordStorageSchemeAddListener (this); 087 rootConfiguration.addPasswordStorageSchemeDeleteListener (this); 088 089 // Initialize existing password storage schemes. 090 for (String schemeName: rootConfiguration.listPasswordStorageSchemes()) 091 { 092 // Get the password storage scheme's configuration. 093 PasswordStorageSchemeCfg config = 094 rootConfiguration.getPasswordStorageScheme (schemeName); 095 096 // Register as a change listener for this password storage scheme 097 // entry so that we will be notified of any changes that may be 098 // made to it. 099 config.addChangeListener (this); 100 101 // Ignore this password storage scheme if it is disabled. 102 if (config.isEnabled()) 103 { 104 // Load the password storage scheme implementation class. 105 String className = config.getJavaClass(); 106 loadAndInstallPasswordStorageScheme (className, config); 107 } 108 } 109 } 110 111 @Override 112 public boolean isConfigurationChangeAcceptable( 113 PasswordStorageSchemeCfg configuration, 114 List<LocalizableMessage> unacceptableReasons 115 ) 116 { 117 // returned status -- all is fine by default 118 boolean status = true; 119 120 if (configuration.isEnabled()) 121 { 122 // Get the name of the class and make sure we can instantiate it as 123 // a password storage scheme. 124 String className = configuration.getJavaClass(); 125 try 126 { 127 // Load the class but don't initialize it. 128 loadPasswordStorageScheme (className, configuration, false); 129 } 130 catch (InitializationException ie) 131 { 132 unacceptableReasons.add(ie.getMessageObject()); 133 status = false; 134 } 135 } 136 137 return status; 138 } 139 140 @Override 141 public ConfigChangeResult applyConfigurationChange( 142 PasswordStorageSchemeCfg configuration 143 ) 144 { 145 final ConfigChangeResult changeResult = new ConfigChangeResult(); 146 147 // Get the configuration entry DN and the associated 148 // password storage scheme class. 149 DN configEntryDN = configuration.dn(); 150 PasswordStorageScheme storageScheme = storageSchemes.get(configEntryDN); 151 152 // If the new configuration has the password storage scheme disabled, 153 // then remove it from the mapping list and clean it. 154 if (! configuration.isEnabled()) 155 { 156 if (storageScheme != null) 157 { 158 uninstallPasswordStorageScheme (configEntryDN); 159 } 160 161 return changeResult; 162 } 163 164 // At this point, new configuration is enabled... 165 // If the current password storage scheme is already enabled then we 166 // don't do anything unless the class has changed in which case we 167 // should indicate that administrative action is required. 168 String newClassName = configuration.getJavaClass(); 169 if (storageScheme != null) 170 { 171 String curClassName = storageScheme.getClass().getName(); 172 boolean classIsNew = !newClassName.equals(curClassName); 173 if (classIsNew) 174 { 175 changeResult.setAdminActionRequired (true); 176 } 177 return changeResult; 178 } 179 180 // New entry cache is enabled and there were no previous one. 181 // Instantiate the new class and initialize it. 182 try 183 { 184 loadAndInstallPasswordStorageScheme (newClassName, configuration); 185 } 186 catch (InitializationException ie) 187 { 188 changeResult.addMessage (ie.getMessageObject()); 189 changeResult.setResultCode (DirectoryServer.getServerErrorResultCode()); 190 return changeResult; 191 } 192 193 return changeResult; 194 } 195 196 @Override 197 public boolean isConfigurationAddAcceptable( 198 PasswordStorageSchemeCfg configuration, 199 List<LocalizableMessage> unacceptableReasons 200 ) 201 { 202 // returned status -- all is fine by default 203 boolean status = true; 204 205 // Make sure that no entry already exists with the specified DN. 206 DN configEntryDN = configuration.dn(); 207 if (storageSchemes.containsKey(configEntryDN)) 208 { 209 unacceptableReasons.add (ERR_CONFIG_PWSCHEME_EXISTS.get(configEntryDN)); 210 status = false; 211 } 212 // If configuration is enabled then check that password storage scheme 213 // class can be instantiated. 214 else if (configuration.isEnabled()) 215 { 216 // Get the name of the class and make sure we can instantiate it as 217 // an entry cache. 218 String className = configuration.getJavaClass(); 219 try 220 { 221 // Load the class but don't initialize it. 222 loadPasswordStorageScheme (className, configuration, false); 223 } 224 catch (InitializationException ie) 225 { 226 unacceptableReasons.add (ie.getMessageObject()); 227 status = false; 228 } 229 } 230 231 return status; 232 } 233 234 @Override 235 public ConfigChangeResult applyConfigurationAdd( 236 PasswordStorageSchemeCfg configuration 237 ) 238 { 239 final ConfigChangeResult changeResult = new ConfigChangeResult(); 240 241 // Register a change listener with it so we can be notified of changes 242 // to it over time. 243 configuration.addChangeListener(this); 244 245 if (configuration.isEnabled()) 246 { 247 // Instantiate the class as password storage scheme 248 // and initialize it. 249 String className = configuration.getJavaClass(); 250 try 251 { 252 loadAndInstallPasswordStorageScheme (className, configuration); 253 } 254 catch (InitializationException ie) 255 { 256 changeResult.addMessage (ie.getMessageObject()); 257 changeResult.setResultCode (DirectoryServer.getServerErrorResultCode()); 258 return changeResult; 259 } 260 } 261 262 return changeResult; 263 } 264 265 @Override 266 public boolean isConfigurationDeleteAcceptable( 267 PasswordStorageSchemeCfg configuration, 268 List<LocalizableMessage> unacceptableReasons 269 ) 270 { 271 // A delete should always be acceptable, so just return true. 272 return true; 273 } 274 275 @Override 276 public ConfigChangeResult applyConfigurationDelete( 277 PasswordStorageSchemeCfg configuration 278 ) 279 { 280 final ConfigChangeResult changeResult = new ConfigChangeResult(); 281 282 uninstallPasswordStorageScheme (configuration.dn()); 283 284 return changeResult; 285 } 286 287 /** 288 * Loads the specified class, instantiates it as a password storage scheme, 289 * and optionally initializes that instance. Any initialized password 290 * storage scheme is registered in the server. 291 * 292 * @param className The fully-qualified name of the password storage 293 * scheme class to load, instantiate, and initialize. 294 * @param configuration The configuration to use to initialize the 295 * password storage scheme, or {@code null} if the 296 * password storage scheme should not be initialized. 297 * 298 * @throws InitializationException If a problem occurred while attempting 299 * to initialize the class. 300 */ 301 private void loadAndInstallPasswordStorageScheme( 302 String className, 303 PasswordStorageSchemeCfg configuration 304 ) 305 throws InitializationException 306 { 307 // Load the password storage scheme class... 308 PasswordStorageScheme 309 <? extends PasswordStorageSchemeCfg> schemeClass; 310 schemeClass = loadPasswordStorageScheme (className, configuration, true); 311 312 // ... and install the password storage scheme in the server. 313 DN configEntryDN = configuration.dn(); 314 storageSchemes.put (configEntryDN, schemeClass); 315 DirectoryServer.registerPasswordStorageScheme (configEntryDN, schemeClass); 316 } 317 318 /** 319 * Loads the specified class, instantiates it as a password storage scheme, 320 * and optionally initializes that instance. 321 * 322 * @param className The fully-qualified name of the class 323 * to load, instantiate, and initialize. 324 * @param configuration The configuration to use to initialize the 325 * class. It must not be {@code null}. 326 * @param initialize Indicates whether the password storage scheme 327 * instance should be initialized. 328 * 329 * @return The possibly initialized password storage scheme. 330 * 331 * @throws InitializationException If a problem occurred while attempting 332 * to initialize the class. 333 */ 334 private <T extends PasswordStorageSchemeCfg> PasswordStorageScheme<T> 335 loadPasswordStorageScheme( 336 String className, 337 T configuration, 338 boolean initialize) 339 throws InitializationException 340 { 341 try 342 { 343 ClassPropertyDefinition propertyDefinition; 344 Class<? extends PasswordStorageScheme> schemeClass; 345 346 PasswordStorageSchemeCfgDefn definition = PasswordStorageSchemeCfgDefn.getInstance(); 347 propertyDefinition = definition.getJavaClassPropertyDefinition(); 348 schemeClass = propertyDefinition.loadClass(className, PasswordStorageScheme.class); 349 PasswordStorageScheme<T> passwordStorageScheme = schemeClass.newInstance(); 350 351 if (initialize) 352 { 353 passwordStorageScheme.initializePasswordStorageScheme(configuration); 354 } 355 else 356 { 357 List<LocalizableMessage> unacceptableReasons = new ArrayList<>(); 358 if (!passwordStorageScheme.isConfigurationAcceptable(configuration, unacceptableReasons)) 359 { 360 String reasons = Utils.joinAsString(". ", unacceptableReasons); 361 throw new InitializationException( 362 ERR_CONFIG_PWSCHEME_CONFIG_NOT_ACCEPTABLE.get(configuration.dn(), reasons)); 363 } 364 } 365 366 return passwordStorageScheme; 367 } 368 catch (Exception e) 369 { 370 LocalizableMessage message = ERR_CONFIG_PWSCHEME_INITIALIZATION_FAILED.get(className, 371 configuration.dn(), stackTraceToSingleLineString(e)); 372 throw new InitializationException(message, e); 373 } 374 } 375 376 /** 377 * Remove a password storage that has been installed in the server. 378 * 379 * @param configEntryDN the DN of the configuration enry associated to 380 * the password storage scheme to remove 381 */ 382 private void uninstallPasswordStorageScheme( 383 DN configEntryDN 384 ) 385 { 386 PasswordStorageScheme scheme = 387 storageSchemes.remove (configEntryDN); 388 if (scheme != null) 389 { 390 DirectoryServer.deregisterPasswordStorageScheme(configEntryDN); 391 scheme.finalizePasswordStorageScheme(); 392 } 393 } 394}