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