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