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