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 2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.extensions;
028    
029    
030    
031    import java.util.Collection;
032    import java.util.LinkedHashSet;
033    import java.util.List;
034    
035    import org.opends.server.admin.std.server.EntryDNVirtualAttributeCfg;
036    import org.opends.server.api.VirtualAttributeProvider;
037    import org.opends.server.config.ConfigException;
038    import org.opends.server.core.DirectoryServer;
039    import org.opends.server.core.SearchOperation;
040    import org.opends.server.loggers.debug.DebugTracer;
041    import org.opends.server.types.AttributeType;
042    import org.opends.server.types.AttributeValue;
043    import org.opends.server.types.ByteString;
044    import org.opends.server.types.ByteStringFactory;
045    import org.opends.server.types.ConditionResult;
046    import org.opends.server.types.DebugLogLevel;
047    import org.opends.server.types.DN;
048    import org.opends.server.types.Entry;
049    import org.opends.server.types.InitializationException;
050    import org.opends.server.types.SearchFilter;
051    import org.opends.server.types.SearchScope;
052    import org.opends.server.types.VirtualAttributeRule;
053    
054    import static org.opends.server.loggers.debug.DebugLogger.*;
055    import static org.opends.server.util.ServerConstants.*;
056    
057    
058    
059    /**
060     * This class implements a virtual attribute provider that is meant to serve the
061     * entryDN operational attribute as described in draft-zeilenga-ldap-entrydn.
062     */
063    public class EntryDNVirtualAttributeProvider
064           extends VirtualAttributeProvider<EntryDNVirtualAttributeCfg>
065    {
066      /**
067       * The tracer object for the debug logger.
068       */
069      private static final DebugTracer TRACER = getTracer();
070    
071      /**
072       * Creates a new instance of this entryDN virtual attribute provider.
073       */
074      public EntryDNVirtualAttributeProvider()
075      {
076        super();
077    
078        // All initialization should be performed in the
079        // initializeVirtualAttributeProvider method.
080      }
081    
082    
083    
084      /**
085       * {@inheritDoc}
086       */
087      @Override()
088      public void initializeVirtualAttributeProvider(
089                                EntryDNVirtualAttributeCfg configuration)
090             throws ConfigException, InitializationException
091      {
092        // No initialization is required.
093      }
094    
095    
096    
097      /**
098       * {@inheritDoc}
099       */
100      @Override()
101      public boolean isMultiValued()
102      {
103        return false;
104      }
105    
106    
107    
108      /**
109       * {@inheritDoc}
110       */
111      @Override()
112      public LinkedHashSet<AttributeValue> getValues(Entry entry,
113                                                     VirtualAttributeRule rule)
114      {
115        LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
116    
117        String normDNString = entry.getDN().toNormalizedString();
118        values.add(new AttributeValue(ByteStringFactory.create(normDNString),
119                                      ByteStringFactory.create(normDNString)));
120    
121        return values;
122      }
123    
124    
125    
126      /**
127       * {@inheritDoc}
128       */
129      @Override()
130      public boolean hasValue(Entry entry, VirtualAttributeRule rule)
131      {
132        // This virtual attribute provider will always generate a value.
133        return true;
134      }
135    
136    
137    
138      /**
139       * {@inheritDoc}
140       */
141      @Override()
142      public boolean hasValue(Entry entry, VirtualAttributeRule rule,
143                              AttributeValue value)
144      {
145        try
146        {
147          String normalizedDN    = entry.getDN().toNormalizedString();
148          String normalizedValue = value.getNormalizedStringValue();
149          return normalizedDN.equals(normalizedValue);
150        }
151        catch (Exception e)
152        {
153          if (debugEnabled())
154          {
155            TRACER.debugCaught(DebugLogLevel.ERROR, e);
156          }
157    
158          return false;
159        }
160      }
161    
162    
163    
164      /**
165       * {@inheritDoc}
166       */
167      @Override()
168      public boolean hasAnyValue(Entry entry, VirtualAttributeRule rule,
169                                 Collection<AttributeValue> values)
170      {
171        String ndnString = entry.getDN().toNormalizedString();
172    
173        AttributeValue v = new AttributeValue(ByteStringFactory.create(ndnString),
174                                              ByteStringFactory.create(ndnString));
175        return values.contains(v);
176      }
177    
178    
179    
180      /**
181       * {@inheritDoc}
182       */
183      @Override()
184      public ConditionResult matchesSubstring(Entry entry,
185                                              VirtualAttributeRule rule,
186                                              ByteString subInitial,
187                                              List<ByteString> subAny,
188                                              ByteString subFinal)
189      {
190        // DNs cannot be used in substring matching.
191        return ConditionResult.UNDEFINED;
192      }
193    
194    
195    
196      /**
197       * {@inheritDoc}
198       */
199      @Override()
200      public ConditionResult greaterThanOrEqualTo(Entry entry,
201                                  VirtualAttributeRule rule,
202                                  AttributeValue value)
203      {
204        // DNs cannot be used in ordering matching.
205        return ConditionResult.UNDEFINED;
206      }
207    
208    
209    
210      /**
211       * {@inheritDoc}
212       */
213      @Override()
214      public ConditionResult lessThanOrEqualTo(Entry entry,
215                                  VirtualAttributeRule rule,
216                                  AttributeValue value)
217      {
218        // DNs cannot be used in ordering matching.
219        return ConditionResult.UNDEFINED;
220      }
221    
222    
223    
224      /**
225       * {@inheritDoc}
226       */
227      @Override()
228      public ConditionResult approximatelyEqualTo(Entry entry,
229                                  VirtualAttributeRule rule,
230                                  AttributeValue value)
231      {
232        // DNs cannot be used in approximate matching.
233        return ConditionResult.UNDEFINED;
234      }
235    
236    
237    
238      /**
239       * {@inheritDoc}.  This virtual attribute will support search operations only
240       * if one of the following is true about the search filter:
241       * <UL>
242       *   <LI>It is an equality filter targeting the associated attribute
243       *       type.</LI>
244       *   <LI>It is an AND filter in which at least one of the components is an
245       *       equality filter targeting the associated attribute type.</LI>
246       *   <LI>It is an OR filter in which all of the components are equality
247       *       filters targeting the associated attribute type.</LI>
248       * </UL>
249       */
250      @Override()
251      public boolean isSearchable(VirtualAttributeRule rule,
252                                  SearchOperation searchOperation)
253      {
254        return isSearchable(rule.getAttributeType(), searchOperation.getFilter(),
255                            0);
256      }
257    
258    
259    
260    
261      /**
262       * Indicates whether the provided search filter is one that may be used with
263       * this virtual attribute provider, optionally operating in a recursive manner
264       * to make the determination.
265       *
266       * @param  attributeType  The attribute type used to hold the entryDN value.
267       * @param  searchFilter   The search filter for which to make the
268       *                        determination.
269       * @param  depth          The current recursion depth for this processing.
270       *
271       * @return  {@code true} if the provided filter may be used with this virtual
272       *          attribute provider, or {@code false} if not.
273       */
274      private boolean isSearchable(AttributeType attributeType, SearchFilter filter,
275                                   int depth)
276      {
277        switch (filter.getFilterType())
278        {
279          case AND:
280            if (depth >= MAX_NESTED_FILTER_DEPTH)
281            {
282              return false;
283            }
284    
285            for (SearchFilter f : filter.getFilterComponents())
286            {
287              if (isSearchable(attributeType, f, depth+1))
288              {
289                return true;
290              }
291            }
292            return false;
293    
294          case OR:
295            if (depth >= MAX_NESTED_FILTER_DEPTH)
296            {
297              return false;
298            }
299    
300            for (SearchFilter f : filter.getFilterComponents())
301            {
302              if (! isSearchable(attributeType, f, depth+1))
303              {
304                return false;
305              }
306            }
307            return true;
308    
309          case EQUALITY:
310            return filter.getAttributeType().equals(attributeType);
311    
312          default:
313            return false;
314        }
315      }
316    
317    
318    
319      /**
320       * {@inheritDoc}
321       */
322      @Override()
323      public void processSearch(VirtualAttributeRule rule,
324                                SearchOperation searchOperation)
325      {
326        SearchFilter      filter = searchOperation.getFilter();
327        LinkedHashSet<DN> dnSet  = new LinkedHashSet<DN>();
328        extractDNs(rule.getAttributeType(), filter, dnSet);
329    
330        if (dnSet.isEmpty())
331        {
332          return;
333        }
334    
335        DN          baseDN = searchOperation.getBaseDN();
336        SearchScope scope  = searchOperation.getScope();
337        for (DN dn : dnSet)
338        {
339          if (! dn.matchesBaseAndScope(baseDN, scope))
340          {
341            continue;
342          }
343    
344          try
345          {
346            Entry entry = DirectoryServer.getEntry(dn);
347            if ((entry != null) && filter.matchesEntry(entry))
348            {
349              searchOperation.returnEntry(entry, null);
350            }
351          }
352          catch (Exception e)
353          {
354            if (debugEnabled())
355            {
356              TRACER.debugCaught(DebugLogLevel.ERROR, e);
357            }
358          }
359        }
360      }
361    
362    
363    
364      /**
365       * Extracts the user DNs from the provided filter, operating recursively as
366       * necessary, and adds them to the provided set.
367       *
368       * @param  attributeType  The attribute type holding the entryDN value.
369       * @param  filter         The search filter to be processed.
370       * @param  dnSet          The set into which the identified DNs should be
371       *                        placed.
372       */
373      private void extractDNs(AttributeType attributeType, SearchFilter filter,
374                              LinkedHashSet<DN> dnSet)
375      {
376        switch (filter.getFilterType())
377        {
378          case AND:
379          case OR:
380            for (SearchFilter f : filter.getFilterComponents())
381            {
382              extractDNs(attributeType, f, dnSet);
383            }
384            break;
385    
386          case EQUALITY:
387            if (filter.getAttributeType().equals(attributeType))
388            {
389              try
390              {
391                dnSet.add(DN.decode(filter.getAssertionValue().getValue()));
392              }
393              catch (Exception e)
394              {
395                if (debugEnabled())
396                {
397                  TRACER.debugCaught(DebugLogLevel.ERROR, e);
398                }
399              }
400            }
401            break;
402        }
403      }
404    }
405