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