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 static org.opends.server.loggers.ErrorLogger.*;
033    import static org.opends.server.loggers.debug.DebugLogger.*;
034    import static org.opends.messages.ConfigMessages.*;
035    import static org.opends.server.util.ServerConstants.*;
036    import static org.opends.server.util.StaticUtils.*;
037    
038    import java.lang.reflect.Method;
039    import java.util.ArrayList;
040    import java.util.Iterator;
041    import java.util.LinkedHashMap;
042    import java.util.List;
043    import java.util.concurrent.atomic.AtomicReference;
044    
045    import org.opends.server.admin.ClassPropertyDefinition;
046    import org.opends.server.admin.server.ConfigurationChangeListener;
047    import org.opends.server.admin.server.ServerManagementContext;
048    import org.opends.server.admin.std.meta.AccessControlHandlerCfgDefn;
049    import org.opends.server.admin.std.server.AccessControlHandlerCfg;
050    import org.opends.server.admin.std.server.RootCfg;
051    import org.opends.server.api.AccessControlHandler;
052    import org.opends.server.api.AlertGenerator;
053    import org.opends.server.config.ConfigException;
054    import org.opends.server.loggers.debug.DebugTracer;
055    import org.opends.server.types.ConfigChangeResult;
056    import org.opends.server.types.DebugLogLevel;
057    import org.opends.server.types.DN;
058    import org.opends.server.types.InitializationException;
059    import org.opends.server.types.ResultCode;
060    
061    
062    
063    /**
064     * This class manages the application-wide access-control configuration.
065     * <p>
066     * When access control is disabled a default "permissive" access control
067     * implementation is used, which permits all operations regardless of the
068     * identity of the user.
069     */
070    public final class AccessControlConfigManager
071           implements AlertGenerator ,
072                      ConfigurationChangeListener<AccessControlHandlerCfg>
073    {
074      /**
075       * The tracer object for the debug logger.
076       */
077      private static final DebugTracer TRACER = getTracer();
078    
079      // Fully qualified class name.
080      private static final String CLASS_NAME =
081        "org.opends.server.core.AccessControlConfigManager";
082    
083      // The single application-wide instance.
084      private static AccessControlConfigManager instance = null;
085    
086      // The active access control implementation.
087      private AtomicReference<AccessControlHandler> accessControlHandler;
088    
089      // The current configuration.
090      private AccessControlHandlerCfg currentConfiguration;
091    
092    
093    
094      /**
095       * Creates a new instance of this access control configuration
096       * manager.
097       */
098      private AccessControlConfigManager()
099      {
100        this.accessControlHandler = new AtomicReference<AccessControlHandler>(
101            new DefaultAccessControlHandler());
102        this.currentConfiguration = null;
103      }
104    
105    
106    
107      /**
108       * Get the single application-wide access control manager instance.
109       *
110       * @return The access control manager.
111       */
112      public static AccessControlConfigManager getInstance()
113      {
114        if (instance == null)
115        {
116          instance = new AccessControlConfigManager();
117        }
118    
119        return instance;
120      }
121    
122    
123    
124      /**
125       * Determine if access control is enabled according to the current
126       * configuration.
127       *
128       * @return  {@code true} if access control is enabled, {@code false}
129       *          otherwise.
130       */
131      public boolean isAccessControlEnabled()
132      {
133        return currentConfiguration.isEnabled();
134      }
135    
136    
137    
138      /**
139       * Get the active access control handler.
140       * <p>
141       * When access control is disabled, this method returns a default access
142       * control implementation which permits all operations.
143       *
144       * @return   The active access control handler (never {@code null}).
145       */
146      public AccessControlHandler getAccessControlHandler()
147      {
148        return accessControlHandler.get();
149      }
150    
151    
152    
153      /**
154       * Initializes the access control sub-system. This should only be
155       * called at Directory Server startup. If an error occurs then an
156       * exception will be thrown and the Directory Server will fail to
157       * start (this prevents accidental exposure of user data due to
158       * misconfiguration).
159       *
160       * @throws ConfigException
161       *           If an access control configuration error is detected.
162       * @throws InitializationException
163       *           If a problem occurs while initializing the access control
164       *           handler that is not related to the Directory Server
165       *           configuration.
166       */
167      public void initializeAccessControl()
168             throws ConfigException, InitializationException
169      {
170        // Get the root configuration object.
171        ServerManagementContext managementContext =
172             ServerManagementContext.getInstance();
173        RootCfg rootConfiguration =
174             managementContext.getRootConfiguration();
175    
176        // Don't register as an add and delete listener with the root configuration
177        // as we can have only one object at a given time.
178    
179        // //Initialize the current Access control.
180        AccessControlHandlerCfg accessControlConfiguration =
181               rootConfiguration.getAccessControlHandler();
182    
183        // We have a valid usable entry, so register a change listener in
184        // order to handle configuration changes.
185        accessControlConfiguration.addChangeListener(this);
186    
187        //This makes TestCaseUtils.reStartServer happy.
188        currentConfiguration=null;
189    
190        // The configuration looks valid, so install it.
191        updateConfiguration(accessControlConfiguration);
192      }
193    
194    
195    
196      /**
197       * Updates the access control configuration based on the contents of a
198       * valid configuration entry.
199       *
200       * @param  newConfiguration  The new configuration object.
201       *
202       * @throws  ConfigException If the access control configuration is invalid.
203       *
204       * @throws  InitializationException  If the access control handler provider
205       *                                   could not be instantiated.
206       */
207    
208      private void updateConfiguration(AccessControlHandlerCfg newConfiguration)
209              throws ConfigException, InitializationException
210      {
211        String newHandlerClass = null;
212        boolean enabledOld = false, enabledNew = newConfiguration.isEnabled();
213    
214        if (currentConfiguration == null)
215        {
216          // Initialization phase.
217          if (enabledNew)
218          {
219            newHandlerClass = newConfiguration.getJavaClass();
220          }
221          else
222          {
223            newHandlerClass = DefaultAccessControlHandler.class.getName();
224          }
225          //Get a new handler, initialize it and make it the current handler.
226          accessControlHandler.getAndSet(getHandler(newHandlerClass,
227                  newConfiguration, true, false));
228        } else {
229          enabledOld = currentConfiguration.isEnabled();
230          if(enabledNew) {
231            //Access control is either being enabled or a attribute in the
232            //configuration has changed such as class name or a global ACI.
233            newHandlerClass = newConfiguration.getJavaClass();
234            String oldHandlerClass = currentConfiguration.getJavaClass();
235            //Check if moving from not enabled to enabled state.
236            if(!enabledOld) {
237               AccessControlHandler oldHandler =
238                       accessControlHandler.getAndSet(getHandler(newHandlerClass,
239                                                      newConfiguration, true,
240                                                      true));
241               oldHandler.finalizeAccessControlHandler();
242            } else {
243              //Check if the class name is being changed.
244              if(!newHandlerClass.equals(oldHandlerClass)) {
245               AccessControlHandler oldHandler =
246                accessControlHandler.getAndSet(getHandler(newHandlerClass,
247                        newConfiguration, true, true));
248                oldHandler.finalizeAccessControlHandler();
249              } else {
250                //Some other attribute has changed, try to get a new non-initialized
251                //handler, but keep the old handler.
252                getHandler(newHandlerClass,newConfiguration, false, false);
253              }
254            }
255          } else if (enabledOld && (! enabledNew)) {
256            //Access control has been disabled, switch to the default handler and
257            //finalize the old handler.
258            newHandlerClass = DefaultAccessControlHandler.class.getName();
259            AccessControlHandler oldHandler =
260                    accessControlHandler.getAndSet(getHandler(newHandlerClass,
261                            newConfiguration, false, true));
262            oldHandler.finalizeAccessControlHandler();
263          }
264        }
265        // Switch in the local configuration.
266        currentConfiguration = newConfiguration;
267      }
268    
269      /**
270       * Instantiates a new Access Control Handler using the specified class name,
271       * configuration.
272       *
273       * @param handlerClassName The name of the handler to instantiate.
274       * @param config The configuration to use when instantiating a new handler.
275       * @param initHandler <code>True</code> if the new handler should be
276       *                    initialized.
277       * @param logMessage <code>True</code> if an error message should be logged
278       *                                     and an alert should be sent.
279       * @return The newly instantiated handler.
280       *
281       * @throws InitializationException  If an error occurs instantiating the
282       *                                  the new handler.
283       */
284      AccessControlHandler<? extends AccessControlHandlerCfg>
285      getHandler(String handlerClassName, AccessControlHandlerCfg config,
286                 boolean initHandler, boolean logMessage)
287              throws InitializationException {
288        AccessControlHandler<? extends AccessControlHandlerCfg> newHandler;
289        try {
290          if(handlerClassName.equals(DefaultAccessControlHandler.class.getName())) {
291            newHandler = new DefaultAccessControlHandler();
292            newHandler.initializeAccessControlHandler(null);
293            if(logMessage) {
294              Message message = WARN_CONFIG_AUTHZ_DISABLED.get();
295              logError(message);
296              if (currentConfiguration != null) {
297                DirectoryServer.sendAlertNotification(this,
298                        ALERT_TYPE_ACCESS_CONTROL_DISABLED, message);
299              }
300            }
301          } else {
302            newHandler = loadHandler(handlerClassName, config, initHandler);
303            if(logMessage) {
304              Message message = NOTE_CONFIG_AUTHZ_ENABLED.get(handlerClassName);
305              logError(message);
306              if (currentConfiguration != null) {
307                DirectoryServer.sendAlertNotification(this,
308                        ALERT_TYPE_ACCESS_CONTROL_ENABLED, message);
309              }
310            }
311          }
312        } catch (Exception e) {
313          if (debugEnabled()) {
314            TRACER.debugCaught(DebugLogLevel.ERROR, e);
315          }
316          Message message = ERR_CONFIG_AUTHZ_UNABLE_TO_INSTANTIATE_HANDLER.
317                  get(handlerClassName, String.valueOf(config.dn().toString()),
318                          stackTraceToSingleLineString(e));
319          throw new InitializationException(message, e);
320        }
321        return newHandler;
322      }
323    
324    
325      /**
326       * {@inheritDoc}
327       */
328      public boolean isConfigurationChangeAcceptable(
329                          AccessControlHandlerCfg configuration,
330                          List<Message> unacceptableReasons)
331      {
332        try
333        {
334          // If the access control handler is disabled, we don't care about the
335          // configuration.  If it is enabled, then all we care about is whether we
336          // can load the access control handler class.
337          if (configuration.isEnabled())
338          {
339            loadHandler(configuration.getJavaClass(), configuration, false);
340          }
341        }
342        catch (InitializationException e)
343        {
344          unacceptableReasons.add(e.getMessageObject());
345          return false;
346        }
347    
348        return true;
349      }
350    
351    
352    
353      /**
354       * {@inheritDoc}
355       */
356      public ConfigChangeResult applyConfigurationChange(
357                                     AccessControlHandlerCfg configuration)
358      {
359        ResultCode resultCode = ResultCode.SUCCESS;
360        ArrayList<Message> messages = new ArrayList<Message>();
361    
362        try
363        {
364          // Attempt to install the new configuration.
365          updateConfiguration(configuration);
366        }
367        catch (ConfigException e)
368        {
369          messages.add(e.getMessageObject());
370          resultCode = ResultCode.CONSTRAINT_VIOLATION;
371        }
372        catch (InitializationException e)
373        {
374          messages.add(e.getMessageObject());
375          resultCode = DirectoryServer.getServerErrorResultCode();
376        }
377    
378        return new ConfigChangeResult(resultCode, false, messages);
379      }
380    
381    
382    
383      /**
384       * {@inheritDoc}
385       */
386      public DN getComponentEntryDN()
387      {
388        return currentConfiguration.dn();
389      }
390    
391    
392    
393      /**
394       * {@inheritDoc}
395       */
396      public String getClassName()
397      {
398        return CLASS_NAME;
399      }
400    
401    
402    
403      /**
404       * {@inheritDoc}
405       */
406      public LinkedHashMap<String,String> getAlerts()
407      {
408        LinkedHashMap<String,String> alerts = new LinkedHashMap<String,String>();
409    
410        alerts.put(ALERT_TYPE_ACCESS_CONTROL_DISABLED,
411                   ALERT_DESCRIPTION_ACCESS_CONTROL_DISABLED);
412        alerts.put(ALERT_TYPE_ACCESS_CONTROL_ENABLED,
413                   ALERT_DESCRIPTION_ACCESS_CONTROL_ENABLED);
414    
415        return alerts;
416      }
417    
418    
419    
420      /**
421       * Loads the specified class, instantiates it as a AccessControlHandler, and
422       * optionally initializes that instance.
423       *
424       * @param  className      The fully-qualified name of the Access Control
425       *                        provider class to load, instantiate, and initialize.
426       * @param  configuration  The configuration to use to initialize the
427       *                        Access Control Handler.  It must not be
428       *                        {@code null}.
429       * @param  initialize     Indicates whether the access control handler
430       *                        instance should be initialized.
431       *
432       * @return  The possibly initialized Access Control Handler.
433       *
434       * @throws  InitializationException  If a problem occurred while attempting to
435       *                                   initialize the Access Control Handler.
436       */
437      private AccessControlHandler<? extends AccessControlHandlerCfg>
438                   loadHandler(String className,
439                               AccessControlHandlerCfg configuration,
440                               boolean initialize)
441              throws InitializationException
442      {
443        try
444        {
445          AccessControlHandlerCfgDefn definition =
446               AccessControlHandlerCfgDefn.getInstance();
447          ClassPropertyDefinition propertyDefinition =
448               definition.getJavaClassPropertyDefinition();
449          Class<? extends AccessControlHandler> providerClass =
450               propertyDefinition.loadClass(className, AccessControlHandler.class);
451          AccessControlHandler<? extends AccessControlHandlerCfg> provider =
452              (AccessControlHandler<? extends AccessControlHandlerCfg>)
453               providerClass.newInstance();
454    
455          if (configuration != null)
456          {
457            Method method = provider.getClass().getMethod(
458                "initializeAccessControlHandler",
459                configuration.configurationClass());
460            if(initialize) {
461              method.invoke(provider, configuration);
462            }
463          }
464          else
465          {
466            Method method =
467                 provider.getClass().getMethod("isConfigurationAcceptable",
468                                               AccessControlHandlerCfg.class,
469                                               List.class);
470    
471            List<Message> unacceptableReasons = new ArrayList<Message>();
472            Boolean acceptable = (Boolean) method.invoke(provider, configuration,
473                                                         unacceptableReasons);
474            if (! acceptable)
475            {
476              StringBuilder buffer = new StringBuilder();
477              if (! unacceptableReasons.isEmpty())
478              {
479                Iterator<Message> iterator = unacceptableReasons.iterator();
480                buffer.append(iterator.next());
481                while (iterator.hasNext())
482                {
483                  buffer.append(".  ");
484                  buffer.append(iterator.next());
485                }
486              }
487    
488              Message message = ERR_CONFIG_AUTHZ_CONFIG_NOT_ACCEPTABLE.get(
489                  String.valueOf(configuration.dn()), buffer.toString());
490              throw new InitializationException(message);
491            }
492          }
493    
494          return provider;
495        }
496        catch (Exception e)
497        {
498          Message message = ERR_CONFIG_AUTHZ_UNABLE_TO_INSTANTIATE_HANDLER.
499              get(className, String.valueOf(configuration.dn()),
500                  stackTraceToSingleLineString(e));
501          throw new InitializationException(message, e);
502        }
503      }
504    }
505