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
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.core;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.lang.reflect.Method;
033    import java.util.ArrayList;
034    import java.util.Iterator;
035    import java.util.List;
036    import java.util.concurrent.ConcurrentHashMap;
037    
038    import org.opends.server.admin.ClassPropertyDefinition;
039    import org.opends.server.admin.server.ConfigurationAddListener;
040    import org.opends.server.admin.server.ConfigurationChangeListener;
041    import org.opends.server.admin.server.ConfigurationDeleteListener;
042    import org.opends.server.admin.std.meta.SASLMechanismHandlerCfgDefn;
043    import org.opends.server.admin.std.server.SASLMechanismHandlerCfg;
044    import org.opends.server.admin.std.server.RootCfg;
045    import org.opends.server.admin.server.ServerManagementContext;
046    import org.opends.server.api.SASLMechanismHandler;
047    import org.opends.server.config.ConfigException;
048    import org.opends.server.types.ConfigChangeResult;
049    import org.opends.server.types.DN;
050    
051    
052    import org.opends.server.types.InitializationException;
053    import org.opends.server.types.ResultCode;
054    
055    import static org.opends.messages.ConfigMessages.*;
056    
057    import static org.opends.server.util.StaticUtils.*;
058    import org.opends.server.loggers.ErrorLogger;
059    
060    
061    /**
062     * This class defines a utility that will be used to manage the set of SASL
063     * mechanism handlers defined in the Directory Server.  It will initialize the
064     * handlers when the server starts, and then will manage any additions,
065     * removals, or modifications to any SASL mechanism handlers while the server is
066     * running.
067     */
068    public class SASLConfigManager implements
069        ConfigurationChangeListener<SASLMechanismHandlerCfg>,
070        ConfigurationAddListener<SASLMechanismHandlerCfg>,
071        ConfigurationDeleteListener<SASLMechanismHandlerCfg>
072    
073    {
074      // A mapping between the DNs of the config entries and the
075      // associated SASL
076      // mechanism handlers.
077      private ConcurrentHashMap<DN,SASLMechanismHandler> handlers;
078    
079    
080    
081      /**
082       * Creates a new instance of this SASL mechanism handler config manager.
083       */
084      public SASLConfigManager()
085      {
086        handlers = new ConcurrentHashMap<DN,SASLMechanismHandler>();
087      }
088    
089    
090    
091      /**
092       * Initializes all SASL mechanism hanlders 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 SASL
097       *                           mechanism handler initialization process to fail.
098       *
099       * @throws  InitializationException  If a problem occurs while initializing
100       *                                   the SASL mechanism handlers that is not
101       *                                   related to the server configuration.
102       */
103      public void initializeSASLMechanismHandlers()
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 SASL mechanism handler entries are added or
115        // removed.
116        rootConfiguration.addSASLMechanismHandlerAddListener(this);
117        rootConfiguration.addSASLMechanismHandlerDeleteListener(this);
118    
119    
120        //Initialize the existing SASL mechanism handlers.
121        for (String handlerName : rootConfiguration.listSASLMechanismHandlers())
122        {
123          SASLMechanismHandlerCfg handlerConfiguration =
124               rootConfiguration.getSASLMechanismHandler(handlerName);
125          handlerConfiguration.addChangeListener(this);
126    
127          if (handlerConfiguration.isEnabled())
128          {
129            String className = handlerConfiguration.getJavaClass();
130            try
131            {
132              SASLMechanismHandler handler = loadHandler(className,
133                                                         handlerConfiguration,
134                                                         true);
135              handlers.put(handlerConfiguration.dn(), handler);
136            }
137            catch (InitializationException ie)
138            {
139              ErrorLogger.logError(ie.getMessageObject());
140              continue;
141            }
142          }
143        }
144      }
145    
146    
147    
148      /**
149       * {@inheritDoc}
150       */
151      public boolean isConfigurationAddAcceptable(
152                          SASLMechanismHandlerCfg configuration,
153                          List<Message> unacceptableReasons)
154      {
155        if (configuration.isEnabled())
156        {
157          // Get the name of the class and make sure we can instantiate it as a SASL
158          // mechanism handler.
159          String className = configuration.getJavaClass();
160          try
161          {
162            loadHandler(className, configuration, false);
163          }
164          catch (InitializationException ie)
165          {
166            unacceptableReasons.add(ie.getMessageObject());
167            return false;
168          }
169        }
170    
171        // If we've gotten here, then it's fine.
172        return true;
173      }
174    
175    
176    
177      /**
178       * {@inheritDoc}
179       */
180      public ConfigChangeResult applyConfigurationAdd(
181                  SASLMechanismHandlerCfg configuration)
182      {
183        ResultCode        resultCode          = ResultCode.SUCCESS;
184        boolean           adminActionRequired = false;
185        ArrayList<Message> messages            = new ArrayList<Message>();
186    
187        configuration.addChangeListener(this);
188    
189        if (! configuration.isEnabled())
190        {
191          return new ConfigChangeResult(resultCode, adminActionRequired, messages);
192        }
193    
194        SASLMechanismHandler handler = null;
195    
196        // Get the name of the class and make sure we can instantiate it as a SASL
197        // mechanism handler.
198        String className = configuration.getJavaClass();
199        try
200        {
201          handler = loadHandler(className, configuration, true);
202        }
203        catch (InitializationException ie)
204        {
205          if (resultCode == ResultCode.SUCCESS)
206          {
207            resultCode = DirectoryServer.getServerErrorResultCode();
208          }
209    
210          messages.add(ie.getMessageObject());
211        }
212    
213        if (resultCode == ResultCode.SUCCESS)
214        {
215          handlers.put(configuration.dn(), handler);
216        }
217    
218        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
219      }
220    
221    
222    
223      /**
224       * {@inheritDoc}
225       */
226      public boolean isConfigurationDeleteAcceptable(
227                          SASLMechanismHandlerCfg configuration,
228                          List<Message> unacceptableReasons)
229      {
230        // FIXME -- We should try to perform some check to determine whether the
231        // SASL mechanism handler is in use.
232        return true;
233      }
234    
235    
236    
237      /**
238       * {@inheritDoc}
239       */
240      public ConfigChangeResult applyConfigurationDelete(
241                  SASLMechanismHandlerCfg configuration)
242      {
243        ResultCode        resultCode          = ResultCode.SUCCESS;
244        boolean           adminActionRequired = false;
245        ArrayList<Message> messages            = new ArrayList<Message>();
246    
247        SASLMechanismHandler handler = handlers.remove(configuration.dn());
248        if (handler != null)
249        {
250          handler.finalizeSASLMechanismHandler();
251        }
252    
253        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
254      }
255    
256    
257    
258      /**
259       * {@inheritDoc}
260       */
261      public boolean isConfigurationChangeAcceptable(
262                          SASLMechanismHandlerCfg configuration,
263                          List<Message> unacceptableReasons)
264      {
265        if (configuration.isEnabled())
266        {
267          // Get the name of the class and make sure we can instantiate it as a SASL
268          // mechanism handler.
269          String className = configuration.getJavaClass();
270          try
271          {
272            loadHandler(className, configuration, false);
273          }
274          catch (InitializationException ie)
275          {
276            unacceptableReasons.add(ie.getMessageObject());
277            return false;
278          }
279        }
280    
281        // If we've gotten here, then it's fine.
282        return true;
283      }
284    
285    
286    
287      /**
288       * {@inheritDoc}
289       */
290      public ConfigChangeResult applyConfigurationChange(
291                  SASLMechanismHandlerCfg configuration)
292      {
293        ResultCode        resultCode          = ResultCode.SUCCESS;
294        boolean           adminActionRequired = false;
295        ArrayList<Message> messages            = new ArrayList<Message>();
296    
297    
298        // Get the existing handler if it's already enabled.
299        SASLMechanismHandler existingHandler = handlers.get(configuration.dn());
300    
301    
302        // If the new configuration has the handler disabled, then disable it if it
303        // is enabled, or do nothing if it's already disabled.
304        if (! configuration.isEnabled())
305        {
306          if (existingHandler != null)
307          {
308            SASLMechanismHandler handler = handlers.remove(configuration.dn());
309            if (handler != null)
310            {
311              handler.finalizeSASLMechanismHandler();
312            }
313          }
314    
315          return new ConfigChangeResult(resultCode, adminActionRequired, messages);
316        }
317    
318    
319        // Get the class for the SASL handler.  If the handler is already enabled,
320        // then we shouldn't do anything with it although if the class has changed
321        // then we'll at least need to indicate that administrative action is
322        // required.  If the handler is disabled, then instantiate the class and
323        // initialize and register it as a SASL mechanism handler.
324        String className = configuration.getJavaClass();
325        if (existingHandler != null)
326        {
327          if (! className.equals(existingHandler.getClass().getName()))
328          {
329            adminActionRequired = true;
330          }
331    
332          return new ConfigChangeResult(resultCode, adminActionRequired, messages);
333        }
334    
335        SASLMechanismHandler handler = null;
336        try
337        {
338          handler = loadHandler(className, configuration, true);
339        }
340        catch (InitializationException ie)
341        {
342          if (resultCode == ResultCode.SUCCESS)
343          {
344            resultCode = DirectoryServer.getServerErrorResultCode();
345          }
346    
347          messages.add(ie.getMessageObject());
348        }
349    
350        if (resultCode == ResultCode.SUCCESS)
351        {
352          handlers.put(configuration.dn(), handler);
353        }
354    
355        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
356      }
357    
358    
359    
360      /**
361       * Loads the specified class, instantiates it as a SASL mechanism hanlder, and
362       * optionally initializes that instance.
363       *
364       * @param  className      The fully-qualified name of the SASL mechanism
365       *                        handler class to load, instantiate, and initialize.
366       * @param  configuration  The configuration to use to initialize the handler.
367       *                        It must not be {@code null}.
368       * @param  initialize     Indicates whether the SASL mechanism handler
369       *                        instance should be initialized.
370       *
371       * @return  The possibly initialized SASL mechanism handler.
372       *
373       * @throws  InitializationException  If a problem occurred while attempting to
374       *                                   initialize the SASL mechanism handler.
375       */
376      private SASLMechanismHandler loadHandler(String className,
377                                               SASLMechanismHandlerCfg
378                                                    configuration,
379                                               boolean initialize)
380              throws InitializationException
381      {
382        try
383        {
384          SASLMechanismHandlerCfgDefn definition =
385               SASLMechanismHandlerCfgDefn.getInstance();
386          ClassPropertyDefinition propertyDefinition =
387               definition.getJavaClassPropertyDefinition();
388          Class<? extends SASLMechanismHandler> handlerClass =
389               propertyDefinition.loadClass(className, SASLMechanismHandler.class);
390          SASLMechanismHandler handler = handlerClass.newInstance();
391    
392          if (initialize)
393          {
394            Method method = handler.getClass().getMethod(
395                "initializeSASLMechanismHandler",
396                configuration.configurationClass());
397            method.invoke(handler, configuration);
398          }
399          else
400          {
401            Method method =
402                 handler.getClass().getMethod("isConfigurationAcceptable",
403                                              SASLMechanismHandlerCfg.class,
404                                              List.class);
405    
406            List<Message> unacceptableReasons = new ArrayList<Message>();
407            Boolean acceptable = (Boolean) method.invoke(handler, configuration,
408                                                         unacceptableReasons);
409            if (! acceptable)
410            {
411              StringBuilder buffer = new StringBuilder();
412              if (! unacceptableReasons.isEmpty())
413              {
414                Iterator<Message> iterator = unacceptableReasons.iterator();
415                buffer.append(iterator.next());
416                while (iterator.hasNext())
417                {
418                  buffer.append(".  ");
419                  buffer.append(iterator.next());
420                }
421              }
422    
423              Message message = ERR_CONFIG_SASL_CONFIG_NOT_ACCEPTABLE.get(
424                  String.valueOf(configuration.dn()), buffer.toString());
425              throw new InitializationException(message);
426            }
427          }
428    
429          return handler;
430        }
431        catch (Exception e)
432        {
433          Message message = ERR_CONFIG_SASL_INITIALIZATION_FAILED.
434              get(className, String.valueOf(configuration.dn()),
435                  stackTraceToSingleLineString(e));
436          throw new InitializationException(message, e);
437        }
438      }
439    }
440