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 2013-2016 ForgeRock AS.
016 */
017package org.opends.server.plugins;
018
019import java.util.LinkedHashSet;
020import java.util.List;
021import java.util.Set;
022
023import org.forgerock.i18n.LocalizableMessage;
024import org.forgerock.i18n.slf4j.LocalizedLogger;
025import org.forgerock.opendj.config.server.ConfigChangeResult;
026import org.forgerock.opendj.config.server.ConfigException;
027import org.forgerock.opendj.config.server.ConfigurationChangeListener;
028import org.forgerock.opendj.ldap.schema.AttributeType;
029import org.forgerock.opendj.ldap.schema.ObjectClass;
030import org.forgerock.opendj.server.config.meta.PluginCfgDefn;
031import org.forgerock.opendj.server.config.server.LDAPAttributeDescriptionListPluginCfg;
032import org.forgerock.opendj.server.config.server.PluginCfg;
033import org.opends.server.api.plugin.DirectoryServerPlugin;
034import org.opends.server.api.plugin.PluginResult;
035import org.opends.server.api.plugin.PluginType;
036import org.opends.server.types.DirectoryConfig;
037import org.opends.server.types.operation.PreParseSearchOperation;
038
039import static org.opends.messages.PluginMessages.*;
040import static org.opends.server.core.DirectoryServer.*;
041import static org.opends.server.util.ServerConstants.*;
042
043/**
044 * This pre-parse plugin modifies the operation to allow an object class
045 * identifier to be specified in attributes lists, such as in Search requests,
046 * to request the return all attributes belonging to an object class as per the
047 * specification in RFC 4529.  The "@" character is used to distinguish an
048 * object class identifier from an attribute descriptions.
049 */
050public final class LDAPADListPlugin
051       extends DirectoryServerPlugin<LDAPAttributeDescriptionListPluginCfg>
052       implements ConfigurationChangeListener<
053                       LDAPAttributeDescriptionListPluginCfg>
054{
055  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
056
057
058
059  /**
060   * Filters the set of attributes provided in a search request or pre- / post-
061   * read controls according to RFC 4529. More specifically, this method
062   * iterates through the requested attributes to see if any of them reference
063   * an object class, as indicated by a "@" prefix, and substitutes the object
064   * class reference with the attribute types contained in the object class, as
065   * well as any of the attribute types contained in any superior object
066   * classes.
067   *
068   * @param attributes
069   *          The attribute list to be normalized.
070   * @return The normalized attribute list.
071   */
072  public static Set<String> normalizedObjectClasses(Set<String> attributes)
073  {
074    boolean foundOC = false;
075    for (String attrName : attributes)
076    {
077      if (attrName.startsWith("@"))
078      {
079        foundOC = true;
080        break;
081      }
082    }
083
084    if (foundOC)
085    {
086      final LinkedHashSet<String> newAttrs = new LinkedHashSet<>();
087      for (final String attrName : attributes)
088      {
089        if (attrName.startsWith("@"))
090        {
091          final String ocName = attrName.substring(1);
092          final ObjectClass oc = getSchema().getObjectClass(ocName);
093          if (oc.isPlaceHolder())
094          {
095            logger.trace("Cannot replace unknown objectclass %s", ocName);
096          }
097          else
098          {
099            logger.trace("Replacing objectclass %s", ocName);
100
101            for (final AttributeType at : oc.getRequiredAttributes())
102            {
103              newAttrs.add(at.getNameOrOID());
104            }
105
106            for (final AttributeType at : oc.getOptionalAttributes())
107            {
108              newAttrs.add(at.getNameOrOID());
109            }
110          }
111        }
112        else
113        {
114          newAttrs.add(attrName);
115        }
116      }
117      attributes = newAttrs;
118    }
119
120    return attributes;
121  }
122
123
124
125  /** The current configuration for this plugin. */
126  private LDAPAttributeDescriptionListPluginCfg currentConfig;
127
128
129
130  /**
131   * Creates a new instance of this Directory Server plugin.  Every plugin must
132   * implement a default constructor (it is the only one that will be used to
133   * create plugins defined in the configuration), and every plugin constructor
134   * must call <CODE>super()</CODE> as its first element.
135   */
136  public LDAPADListPlugin()
137  {
138    super();
139  }
140
141
142
143  @Override
144  public final void initializePlugin(Set<PluginType> pluginTypes,
145                         LDAPAttributeDescriptionListPluginCfg configuration)
146         throws ConfigException
147  {
148    currentConfig = configuration;
149    configuration.addLDAPAttributeDescriptionListChangeListener(this);
150
151    // The set of plugin types must contain only the pre-parse search element.
152    if (pluginTypes.isEmpty())
153    {
154      throw new ConfigException(ERR_PLUGIN_ADLIST_NO_PLUGIN_TYPES.get(configuration.dn()));
155    }
156    else
157    {
158      for (PluginType t : pluginTypes)
159      {
160        if (t != PluginType.PRE_PARSE_SEARCH)
161        {
162          throw new ConfigException(ERR_PLUGIN_ADLIST_INVALID_PLUGIN_TYPE.get(configuration.dn(), t));
163        }
164      }
165    }
166
167
168    // Register the appropriate supported feature with the Directory Server.
169    DirectoryConfig.registerSupportedFeature(OID_LDAP_ADLIST_FEATURE);
170  }
171
172
173
174  @Override
175  public final void finalizePlugin()
176  {
177    currentConfig.removeLDAPAttributeDescriptionListChangeListener(this);
178  }
179
180
181
182  @Override
183  public final PluginResult.PreParse doPreParse(
184      PreParseSearchOperation searchOperation)
185  {
186    searchOperation.setAttributes(normalizedObjectClasses(searchOperation
187        .getAttributes()));
188    return PluginResult.PreParse.continueOperationProcessing();
189  }
190
191
192
193  @Override
194  public boolean isConfigurationAcceptable(PluginCfg configuration,
195                                           List<LocalizableMessage> unacceptableReasons)
196  {
197    LDAPAttributeDescriptionListPluginCfg cfg =
198         (LDAPAttributeDescriptionListPluginCfg) configuration;
199    return isConfigurationChangeAcceptable(cfg, unacceptableReasons);
200  }
201
202
203
204  @Override
205  public boolean isConfigurationChangeAcceptable(
206                      LDAPAttributeDescriptionListPluginCfg configuration,
207                      List<LocalizableMessage> unacceptableReasons)
208  {
209    boolean configAcceptable = true;
210
211    // Ensure that the set of plugin types contains only pre-parse search.
212    for (PluginCfgDefn.PluginType pluginType : configuration.getPluginType())
213    {
214      switch (pluginType)
215      {
216        case PREPARSESEARCH:
217          // This is acceptable.
218          break;
219
220
221        default:
222          unacceptableReasons.add(ERR_PLUGIN_ADLIST_INVALID_PLUGIN_TYPE.get(configuration.dn(), pluginType));
223          configAcceptable = false;
224      }
225    }
226
227    return configAcceptable;
228  }
229
230
231
232  @Override
233  public ConfigChangeResult applyConfigurationChange(
234                                 LDAPAttributeDescriptionListPluginCfg
235                                      configuration)
236  {
237    currentConfig = configuration;
238    return new ConfigChangeResult();
239  }
240}
241