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