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