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