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 2008-2009 Sun Microsystems, Inc.
015 * Portions Copyright 2012-2016 ForgeRock AS.
016 */
017package org.opends.server.extensions;
018
019import java.util.LinkedHashSet;
020import java.util.List;
021import java.util.Set;
022
023import org.forgerock.i18n.LocalizedIllegalArgumentException;
024import org.forgerock.i18n.slf4j.LocalizedLogger;
025import org.forgerock.opendj.ldap.ByteString;
026import org.forgerock.opendj.ldap.ConditionResult;
027import org.forgerock.opendj.ldap.DN;
028import org.forgerock.opendj.ldap.DecodeException;
029import org.forgerock.opendj.ldap.SearchScope;
030import org.forgerock.opendj.ldap.schema.AttributeType;
031import org.forgerock.opendj.ldap.schema.MatchingRule;
032import org.opends.server.admin.std.server.EntryDNVirtualAttributeCfg;
033import org.opends.server.api.VirtualAttributeProvider;
034import org.opends.server.core.DirectoryServer;
035import org.opends.server.core.SearchOperation;
036import org.opends.server.types.Attribute;
037import org.opends.server.types.Attributes;
038import org.opends.server.types.Entry;
039import org.opends.server.types.SearchFilter;
040import org.opends.server.types.VirtualAttributeRule;
041
042import static org.opends.server.util.ServerConstants.*;
043
044/**
045 * This class implements a virtual attribute provider that is meant to serve the
046 * entryDN operational attribute as described in draft-zeilenga-ldap-entrydn.
047 */
048public class EntryDNVirtualAttributeProvider
049       extends VirtualAttributeProvider<EntryDNVirtualAttributeCfg>
050{
051  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
052
053  /**
054   * Creates a new instance of this entryDN virtual attribute provider.
055   */
056  public EntryDNVirtualAttributeProvider()
057  {
058    super();
059
060    // All initialization should be performed in the
061    // initializeVirtualAttributeProvider method.
062  }
063
064  /** {@inheritDoc} */
065  @Override
066  public boolean isMultiValued()
067  {
068    return false;
069  }
070
071  /** {@inheritDoc} */
072  @Override
073  public Attribute getValues(Entry entry, VirtualAttributeRule rule)
074  {
075    String dnString = entry.getName().toString();
076    return Attributes.create(rule.getAttributeType(), dnString);
077  }
078
079  /** {@inheritDoc} */
080  @Override
081  public boolean hasValue(Entry entry, VirtualAttributeRule rule)
082  {
083    // This virtual attribute provider will always generate a value.
084    return true;
085  }
086
087  /** {@inheritDoc} */
088  @Override
089  public boolean hasValue(Entry entry, VirtualAttributeRule rule, ByteString value)
090  {
091    try
092    {
093      MatchingRule eqRule = rule.getAttributeType().getEqualityMatchingRule();
094      ByteString dn = ByteString.valueOfUtf8(entry.getName().toString());
095      ByteString normalizedDN = eqRule.normalizeAttributeValue(dn);
096      ByteString normalizedValue = eqRule.normalizeAttributeValue(value);
097      return normalizedDN.equals(normalizedValue);
098    }
099    catch (DecodeException e)
100    {
101      logger.traceException(e);
102      return false;
103    }
104  }
105
106  /** {@inheritDoc} */
107  @Override
108  public ConditionResult matchesSubstring(Entry entry,
109                                          VirtualAttributeRule rule,
110                                          ByteString subInitial,
111                                          List<ByteString> subAny,
112                                          ByteString subFinal)
113  {
114    // DNs cannot be used in substring matching.
115    return ConditionResult.UNDEFINED;
116  }
117
118  /** {@inheritDoc} */
119  @Override
120  public ConditionResult greaterThanOrEqualTo(Entry entry,
121                              VirtualAttributeRule rule,
122                              ByteString value)
123  {
124    // DNs cannot be used in ordering matching.
125    return ConditionResult.UNDEFINED;
126  }
127
128  /** {@inheritDoc} */
129  @Override
130  public ConditionResult lessThanOrEqualTo(Entry entry,
131                              VirtualAttributeRule rule,
132                              ByteString value)
133  {
134    // DNs cannot be used in ordering matching.
135    return ConditionResult.UNDEFINED;
136  }
137
138  /** {@inheritDoc} */
139  @Override
140  public ConditionResult approximatelyEqualTo(Entry entry,
141                              VirtualAttributeRule rule,
142                              ByteString value)
143  {
144    // DNs cannot be used in approximate matching.
145    return ConditionResult.UNDEFINED;
146  }
147
148
149
150  /**
151   * {@inheritDoc}.  This virtual attribute will support search operations only
152   * if one of the following is true about the search filter:
153   * <UL>
154   *   <LI>It is an equality filter targeting the associated attribute
155   *       type.</LI>
156   *   <LI>It is an AND filter in which at least one of the components is an
157   *       equality filter targeting the associated attribute type.</LI>
158   *   <LI>It is an OR filter in which all of the components are equality
159   *       filters targeting the associated attribute type.</LI>
160   * </UL>
161   * This virtual attribute also can be optimized as pre-indexed.
162   */
163  @Override
164  public boolean isSearchable(VirtualAttributeRule rule,
165                              SearchOperation searchOperation,
166                              boolean isPreIndexed)
167  {
168    return isSearchable(rule.getAttributeType(), searchOperation.getFilter(), 0);
169  }
170
171
172
173
174  /**
175   * Indicates whether the provided search filter is one that may be used with
176   * this virtual attribute provider, optionally operating in a recursive manner
177   * to make the determination.
178   *
179   * @param  attributeType  The attribute type used to hold the entryDN value.
180   * @param  searchFilter   The search filter for which to make the
181   *                        determination.
182   * @param  depth          The current recursion depth for this processing.
183   *
184   * @return  {@code true} if the provided filter may be used with this virtual
185   *          attribute provider, or {@code false} if not.
186   */
187  private boolean isSearchable(AttributeType attributeType, SearchFilter filter,
188                               int depth)
189  {
190    switch (filter.getFilterType())
191    {
192      case AND:
193        if (depth >= MAX_NESTED_FILTER_DEPTH)
194        {
195          return false;
196        }
197
198        for (SearchFilter f : filter.getFilterComponents())
199        {
200          if (isSearchable(attributeType, f, depth+1))
201          {
202            return true;
203          }
204        }
205        return false;
206
207      case OR:
208        if (depth >= MAX_NESTED_FILTER_DEPTH)
209        {
210          return false;
211        }
212
213        for (SearchFilter f : filter.getFilterComponents())
214        {
215          if (! isSearchable(attributeType, f, depth+1))
216          {
217            return false;
218          }
219        }
220        return true;
221
222      case EQUALITY:
223        return filter.getAttributeType().equals(attributeType);
224
225      default:
226        return false;
227    }
228  }
229
230  /** {@inheritDoc} */
231  @Override
232  public void processSearch(VirtualAttributeRule rule,
233                            SearchOperation searchOperation)
234  {
235    SearchFilter      filter = searchOperation.getFilter();
236    Set<DN> dnSet = new LinkedHashSet<>();
237    extractDNs(rule.getAttributeType(), filter, dnSet);
238
239    if (dnSet.isEmpty())
240    {
241      return;
242    }
243
244    DN          baseDN = searchOperation.getBaseDN();
245    SearchScope scope  = searchOperation.getScope();
246    for (DN dn : dnSet)
247    {
248      if (! dn.isInScopeOf(baseDN, scope))
249      {
250        continue;
251      }
252
253      try
254      {
255        Entry entry = DirectoryServer.getEntry(dn);
256        if (entry != null && filter.matchesEntry(entry))
257        {
258          searchOperation.returnEntry(entry, null);
259        }
260      }
261      catch (Exception e)
262      {
263        logger.traceException(e);
264      }
265    }
266  }
267
268
269
270  /**
271   * Extracts the user DNs from the provided filter, operating recursively as
272   * necessary, and adds them to the provided set.
273   *
274   * @param  attributeType  The attribute type holding the entryDN value.
275   * @param  filter         The search filter to be processed.
276   * @param  dnSet          The set into which the identified DNs should be
277   *                        placed.
278   */
279  private void extractDNs(AttributeType attributeType, SearchFilter filter, Set<DN> dnSet)
280  {
281    switch (filter.getFilterType())
282    {
283      case AND:
284      case OR:
285        for (SearchFilter f : filter.getFilterComponents())
286        {
287          extractDNs(attributeType, f, dnSet);
288        }
289        break;
290
291      case EQUALITY:
292        if (filter.getAttributeType().equals(attributeType))
293        {
294          try
295          {
296            dnSet.add(DN.valueOf(filter.getAssertionValue()));
297          }
298          catch (LocalizedIllegalArgumentException e)
299          {
300            logger.traceException(e);
301          }
302        }
303        break;
304    }
305  }
306}