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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2008 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027package org.opends.server.core; 028 029import static org.opends.messages.ConfigMessages.*; 030import static org.opends.server.util.StaticUtils.*; 031 032import java.util.ArrayList; 033import java.util.List; 034import java.util.concurrent.ConcurrentHashMap; 035 036import org.forgerock.i18n.LocalizableMessage; 037import org.forgerock.i18n.slf4j.LocalizedLogger; 038import org.forgerock.opendj.config.server.ConfigException; 039import org.forgerock.opendj.ldap.ResultCode; 040import org.forgerock.util.Utils; 041import org.opends.server.admin.ClassPropertyDefinition; 042import org.opends.server.admin.server.ConfigurationAddListener; 043import org.opends.server.admin.server.ConfigurationChangeListener; 044import org.opends.server.admin.server.ConfigurationDeleteListener; 045import org.opends.server.admin.server.ServerManagementContext; 046import org.opends.server.admin.std.meta.SASLMechanismHandlerCfgDefn; 047import org.opends.server.admin.std.server.RootCfg; 048import org.opends.server.admin.std.server.SASLMechanismHandlerCfg; 049import org.opends.server.api.SASLMechanismHandler; 050import org.forgerock.opendj.config.server.ConfigChangeResult; 051import org.opends.server.types.DN; 052import org.opends.server.types.InitializationException; 053 054/** 055 * This class defines a utility that will be used to manage the set of SASL 056 * mechanism handlers defined in the Directory Server. It will initialize the 057 * handlers when the server starts, and then will manage any additions, 058 * removals, or modifications to any SASL mechanism handlers while the server is 059 * running. 060 */ 061public class SASLConfigManager implements 062 ConfigurationChangeListener<SASLMechanismHandlerCfg>, 063 ConfigurationAddListener<SASLMechanismHandlerCfg>, 064 ConfigurationDeleteListener<SASLMechanismHandlerCfg> 065{ 066 067 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 068 069 /** 070 * A mapping between the DNs of the config entries and the associated SASL 071 * mechanism handlers. 072 */ 073 private final ConcurrentHashMap<DN, SASLMechanismHandler> handlers; 074 075 private final ServerContext serverContext; 076 077 /** 078 * Creates a new instance of this SASL mechanism handler config manager. 079 * 080 * @param serverContext 081 * The server context. 082 */ 083 public SASLConfigManager(ServerContext serverContext) 084 { 085 this.serverContext = serverContext; 086 handlers = new ConcurrentHashMap<>(); 087 } 088 089 090 091 /** 092 * Initializes all SASL mechanism handlers currently defined in the Directory 093 * Server configuration. This should only be called at Directory Server 094 * startup. 095 * 096 * @throws ConfigException If a configuration problem causes the SASL 097 * mechanism handler initialization process to fail. 098 * 099 * @throws InitializationException If a problem occurs while initializing 100 * the SASL mechanism handlers that is not 101 * related to the server configuration. 102 */ 103 public void initializeSASLMechanismHandlers() 104 throws ConfigException, InitializationException 105 { 106 // Get the root configuration object. 107 ServerManagementContext managementContext = 108 ServerManagementContext.getInstance(); 109 RootCfg rootConfiguration = 110 managementContext.getRootConfiguration(); 111 112 113 // Register as an add and delete listener with the root configuration so we 114 // can be notified if any SASL mechanism handler entries are added or 115 // removed. 116 rootConfiguration.addSASLMechanismHandlerAddListener(this); 117 rootConfiguration.addSASLMechanismHandlerDeleteListener(this); 118 119 120 //Initialize the existing SASL mechanism handlers. 121 for (String handlerName : rootConfiguration.listSASLMechanismHandlers()) 122 { 123 SASLMechanismHandlerCfg handlerConfiguration = 124 rootConfiguration.getSASLMechanismHandler(handlerName); 125 handlerConfiguration.addChangeListener(this); 126 127 if (handlerConfiguration.isEnabled()) 128 { 129 String className = handlerConfiguration.getJavaClass(); 130 try 131 { 132 SASLMechanismHandler handler = loadHandler(className, 133 handlerConfiguration, 134 true); 135 handlers.put(handlerConfiguration.dn(), handler); 136 } 137 catch (InitializationException ie) 138 { 139 logger.error(ie.getMessageObject()); 140 continue; 141 } 142 } 143 } 144 } 145 146 147 148 /** {@inheritDoc} */ 149 @Override 150 public boolean isConfigurationAddAcceptable( 151 SASLMechanismHandlerCfg configuration, 152 List<LocalizableMessage> unacceptableReasons) 153 { 154 if (configuration.isEnabled()) 155 { 156 // Get the name of the class and make sure we can instantiate it as a SASL 157 // mechanism handler. 158 String className = configuration.getJavaClass(); 159 try 160 { 161 loadHandler(className, configuration, false); 162 } 163 catch (InitializationException ie) 164 { 165 unacceptableReasons.add(ie.getMessageObject()); 166 return false; 167 } 168 } 169 170 // If we've gotten here, then it's fine. 171 return true; 172 } 173 174 175 176 /** {@inheritDoc} */ 177 @Override 178 public ConfigChangeResult applyConfigurationAdd( 179 SASLMechanismHandlerCfg configuration) 180 { 181 final ConfigChangeResult ccr = new ConfigChangeResult(); 182 183 configuration.addChangeListener(this); 184 185 if (! configuration.isEnabled()) 186 { 187 return ccr; 188 } 189 190 SASLMechanismHandler handler = null; 191 192 // Get the name of the class and make sure we can instantiate it as a SASL 193 // mechanism handler. 194 String className = configuration.getJavaClass(); 195 try 196 { 197 handler = loadHandler(className, configuration, true); 198 } 199 catch (InitializationException ie) 200 { 201 ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode()); 202 ccr.addMessage(ie.getMessageObject()); 203 } 204 205 if (ccr.getResultCode() == ResultCode.SUCCESS) 206 { 207 handlers.put(configuration.dn(), handler); 208 } 209 210 return ccr; 211 } 212 213 214 215 /** {@inheritDoc} */ 216 @Override 217 public boolean isConfigurationDeleteAcceptable( 218 SASLMechanismHandlerCfg configuration, 219 List<LocalizableMessage> unacceptableReasons) 220 { 221 // FIXME -- We should try to perform some check to determine whether the 222 // SASL mechanism handler is in use. 223 return true; 224 } 225 226 227 228 /** {@inheritDoc} */ 229 @Override 230 public ConfigChangeResult applyConfigurationDelete( 231 SASLMechanismHandlerCfg configuration) 232 { 233 final ConfigChangeResult ccr = new ConfigChangeResult(); 234 235 SASLMechanismHandler handler = handlers.remove(configuration.dn()); 236 if (handler != null) 237 { 238 handler.finalizeSASLMechanismHandler(); 239 } 240 241 return ccr; 242 } 243 244 245 246 /** {@inheritDoc} */ 247 @Override 248 public boolean isConfigurationChangeAcceptable( 249 SASLMechanismHandlerCfg configuration, 250 List<LocalizableMessage> unacceptableReasons) 251 { 252 if (configuration.isEnabled()) 253 { 254 // Get the name of the class and make sure we can instantiate it as a SASL 255 // mechanism handler. 256 String className = configuration.getJavaClass(); 257 try 258 { 259 loadHandler(className, configuration, false); 260 } 261 catch (InitializationException ie) 262 { 263 unacceptableReasons.add(ie.getMessageObject()); 264 return false; 265 } 266 } 267 268 // If we've gotten here, then it's fine. 269 return true; 270 } 271 272 273 274 /** {@inheritDoc} */ 275 @Override 276 public ConfigChangeResult applyConfigurationChange( 277 SASLMechanismHandlerCfg configuration) 278 { 279 final ConfigChangeResult ccr = new ConfigChangeResult(); 280 281 282 // Get the existing handler if it's already enabled. 283 SASLMechanismHandler existingHandler = handlers.get(configuration.dn()); 284 285 286 // If the new configuration has the handler disabled, then disable it if it 287 // is enabled, or do nothing if it's already disabled. 288 if (! configuration.isEnabled()) 289 { 290 if (existingHandler != null) 291 { 292 SASLMechanismHandler handler = handlers.remove(configuration.dn()); 293 if (handler != null) 294 { 295 handler.finalizeSASLMechanismHandler(); 296 } 297 } 298 299 return ccr; 300 } 301 302 303 // Get the class for the SASL handler. If the handler is already enabled, 304 // then we shouldn't do anything with it although if the class has changed 305 // then we'll at least need to indicate that administrative action is 306 // required. If the handler is disabled, then instantiate the class and 307 // initialize and register it as a SASL mechanism handler. 308 String className = configuration.getJavaClass(); 309 if (existingHandler != null) 310 { 311 if (! className.equals(existingHandler.getClass().getName())) 312 { 313 ccr.setAdminActionRequired(true); 314 } 315 316 return ccr; 317 } 318 319 SASLMechanismHandler handler = null; 320 try 321 { 322 handler = loadHandler(className, configuration, true); 323 } 324 catch (InitializationException ie) 325 { 326 ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode()); 327 ccr.addMessage(ie.getMessageObject()); 328 } 329 330 if (ccr.getResultCode() == ResultCode.SUCCESS) 331 { 332 handlers.put(configuration.dn(), handler); 333 } 334 335 return ccr; 336 } 337 338 339 340 /** 341 * Loads the specified class, instantiates it as a SASL mechanism hanlder, and 342 * optionally initializes that instance. 343 * 344 * @param className The fully-qualified name of the SASL mechanism 345 * handler class to load, instantiate, and initialize. 346 * @param configuration The configuration to use to initialize the handler. 347 * It must not be {@code null}. 348 * @param initialize Indicates whether the SASL mechanism handler 349 * instance should be initialized. 350 * 351 * @return The possibly initialized SASL mechanism handler. 352 * 353 * @throws InitializationException If a problem occurred while attempting to 354 * initialize the SASL mechanism handler. 355 */ 356 private SASLMechanismHandler loadHandler(String className, 357 SASLMechanismHandlerCfg 358 configuration, 359 boolean initialize) 360 throws InitializationException 361 { 362 try 363 { 364 SASLMechanismHandlerCfgDefn definition = 365 SASLMechanismHandlerCfgDefn.getInstance(); 366 ClassPropertyDefinition propertyDefinition = 367 definition.getJavaClassPropertyDefinition(); 368 Class<? extends SASLMechanismHandler> handlerClass = 369 propertyDefinition.loadClass(className, SASLMechanismHandler.class); 370 SASLMechanismHandler handler = handlerClass.newInstance(); 371 372 if (initialize) 373 { 374 handler.initializeSASLMechanismHandler(configuration); 375 } 376 else 377 { 378 List<LocalizableMessage> unacceptableReasons = new ArrayList<>(); 379 if (!handler.isConfigurationAcceptable(configuration, unacceptableReasons)) 380 { 381 String reasons = Utils.joinAsString(". ", unacceptableReasons); 382 throw new InitializationException( 383 ERR_CONFIG_SASL_CONFIG_NOT_ACCEPTABLE.get(configuration.dn(), reasons)); 384 } 385 } 386 387 return handler; 388 } 389 catch (Exception e) 390 { 391 LocalizableMessage message = ERR_CONFIG_SASL_INITIALIZATION_FAILED. 392 get(className, configuration.dn(), stackTraceToSingleLineString(e)); 393 throw new InitializationException(message, e); 394 } 395 } 396} 397