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.controls;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.util.ArrayList;
033    import java.util.Iterator;
034    import java.util.LinkedHashSet;
035    
036    import org.opends.server.core.DirectoryServer;
037    import org.opends.server.protocols.asn1.ASN1Element;
038    import org.opends.server.protocols.asn1.ASN1Exception;
039    import org.opends.server.protocols.asn1.ASN1OctetString;
040    import org.opends.server.protocols.asn1.ASN1Sequence;
041    import org.opends.server.protocols.ldap.LDAPResultCode;
042    import org.opends.server.types.AttributeType;
043    import org.opends.server.types.Control;
044    import org.opends.server.types.ObjectClass;
045    import org.opends.server.types.DebugLogLevel;
046    import org.opends.server.types.LDAPException;
047    
048    import static org.opends.server.loggers.debug.DebugLogger.*;
049    import org.opends.server.loggers.debug.DebugTracer;
050    import static org.opends.messages.ProtocolMessages.*;
051    import static org.opends.server.util.ServerConstants.*;
052    
053    
054    
055    /**
056     * This class implements the pre-read request control as defined in RFC 4527.
057     * This control makes it possible to retrieve an entry in the state that it held
058     * immediately before a modify, delete, or modify DN operation.  It may specify
059     * a specific set of attributes that should be included in that entry.  The
060     * entry will be encoded in a corresponding response control.
061     */
062    public class LDAPPreReadRequestControl
063           extends Control
064    {
065      /**
066       * The tracer object for the debug logger.
067       */
068      private static final DebugTracer TRACER = getTracer();
069    
070    
071    
072    
073      // Indicates whether the request indicates that all operational attributes
074      // should be returned.
075      private boolean returnAllOperationalAttrs;
076    
077      // Indicates whether the request indicates that all user attributes should be
078      // returned.
079      private boolean returnAllUserAttrs;
080    
081      // The set of raw attributes to return in the entry.
082      private LinkedHashSet<String> rawAttributes;
083    
084      // The set of processed attributes to return in the entry.
085      private LinkedHashSet<AttributeType> requestedAttributes;
086    
087    
088    
089      /**
090       * Creates a new instance of this LDAP pre-read request control with the
091       * provided information.
092       *
093       * @param  isCritical     Indicates whether support for this control should be
094       *                        considered a critical part of the server processing.
095       * @param  rawAttributes  The set of raw attributes to return in the entry.
096       *                        A null or empty set will indicates that all user
097       *                        attributes should be returned.
098       */
099      public LDAPPreReadRequestControl(boolean isCritical,
100                                       LinkedHashSet<String> rawAttributes)
101      {
102        super(OID_LDAP_READENTRY_PREREAD, isCritical,
103              encodeAttributes(rawAttributes));
104    
105    
106        if (rawAttributes == null)
107        {
108          this.rawAttributes = new LinkedHashSet<String>(0);
109        }
110        else
111        {
112          this.rawAttributes = rawAttributes;
113        }
114    
115        requestedAttributes       = null;
116        returnAllOperationalAttrs = false;
117        returnAllUserAttrs        = false;
118      }
119    
120    
121    
122      /**
123       * Creates a new instance of this LDAP pre-read request control with the
124       * provided information.
125       *
126       * @param  oid            The OID to use for this control.
127       * @param  isCritical     Indicates whether support for this control should be
128       *                        considered a critical part of the server processing.
129       * @param  rawAttributes  The set of raw attributes to return in the entry.
130       *                        A null or empty set will indicates that all user
131       *                        attributes should be returned.
132       */
133      public LDAPPreReadRequestControl(String oid, boolean isCritical,
134                                       LinkedHashSet<String> rawAttributes)
135      {
136        super(oid, isCritical, encodeAttributes(rawAttributes));
137    
138    
139        if (rawAttributes == null)
140        {
141          this.rawAttributes = new LinkedHashSet<String>(0);
142        }
143        else
144        {
145          this.rawAttributes = rawAttributes;
146        }
147    
148        requestedAttributes       = null;
149        returnAllOperationalAttrs = false;
150        returnAllUserAttrs        = false;
151      }
152    
153    
154    
155      /**
156       * Creates a new instance of this LDAP pre-read request control with the
157       * provided information.
158       *
159       * @param  oid            The OID to use for this control.
160       * @param  isCritical     Indicates whether support for this control should be
161       *                        considered a critical part of the server processing.
162       * @param  rawAttributes  The set of raw attributes to return in the entry.
163       *                        A null or empty set will indicates that all user
164       *                        attributes should be returned.
165       * @param  encodedValue   The pre-encoded value for this control.
166       */
167      private LDAPPreReadRequestControl(String oid, boolean isCritical,
168                                        LinkedHashSet<String> rawAttributes,
169                                        ASN1OctetString encodedValue)
170      {
171        super(oid, isCritical, encodedValue);
172    
173    
174        if (rawAttributes == null)
175        {
176          this.rawAttributes = new LinkedHashSet<String>(0);
177        }
178        else
179        {
180          this.rawAttributes = rawAttributes;
181        }
182    
183        requestedAttributes       = null;
184        returnAllOperationalAttrs = false;
185        returnAllUserAttrs        = false;
186      }
187    
188    
189    
190      /**
191       * Creates a new LDAP pre-read request control from the contents of the
192       * provided control.
193       *
194       * @param  control  The generic control containing the information to use to
195       *                  create this LDAP pre-read request control.
196       *
197       * @return  The LDAP pre-read request control decoded from the provided
198       *          control.
199       *
200       * @throws  LDAPException  If this control cannot be decoded as a valid LDAP
201       *                         pre-read request control.
202       */
203      public static LDAPPreReadRequestControl decodeControl(Control control)
204             throws LDAPException
205      {
206        if (! control.hasValue())
207        {
208          Message message = ERR_PREREADREQ_NO_CONTROL_VALUE.get();
209          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
210        }
211    
212    
213        LinkedHashSet<String> rawAttributes = new LinkedHashSet<String>();
214        try
215        {
216          ASN1Sequence attrSequence =
217               ASN1Sequence.decodeAsSequence(control.getValue().value());
218          for (ASN1Element e : attrSequence.elements())
219          {
220            rawAttributes.add(e.decodeAsOctetString().stringValue());
221          }
222        }
223        catch (ASN1Exception ae)
224        {
225          if (debugEnabled())
226          {
227            TRACER.debugCaught(DebugLogLevel.ERROR, ae);
228          }
229    
230          Message message = ERR_PREREADREQ_CANNOT_DECODE_VALUE.get(ae.getMessage());
231          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message,
232                                  ae);
233        }
234    
235    
236        return new LDAPPreReadRequestControl(control.getOID(), control.isCritical(),
237                                             rawAttributes, control.getValue());
238      }
239    
240    
241    
242      /**
243       * Encodes the provided set of raw, unprocessed attribute names to an
244       * ASN.1 octet string suitable for use as the value of this control.
245       *
246       * @param  rawAttributes  The set of attributes to encoded in the encoded
247       *                        control value.
248       *
249       * @return  The ASN.1 octet string containing the encoded attribute set.
250       */
251      private static ASN1OctetString encodeAttributes(LinkedHashSet<String>
252                                                           rawAttributes)
253      {
254        if (rawAttributes == null)
255        {
256          return new ASN1OctetString(new ASN1Sequence().encode());
257        }
258    
259        ArrayList<ASN1Element> elements =
260             new ArrayList<ASN1Element>(rawAttributes.size());
261        for (String attr : rawAttributes)
262        {
263          elements.add(new ASN1OctetString(attr));
264        }
265    
266        return new ASN1OctetString(new ASN1Sequence(elements).encode());
267      }
268    
269    
270    
271      /**
272       * Retrieves the raw, unprocessed set of requested attributes.  It must not
273       * be altered by the caller without calling <CODE>setRawAttributes</CODE> with
274       * the updated set.
275       *
276       * @return  The raw, unprocessed set of attributes.
277       */
278      public LinkedHashSet<String> getRawAttributes()
279      {
280        return rawAttributes;
281      }
282    
283    
284    
285      /**
286       * Specifies the raw, unprocessed set of requested attributes.  A null or
287       * empty set indicates that all user attributes should be returned.
288       *
289       * @param  rawAttributes  The raw, unprocessed set of requested attributes.
290       */
291      public void setRawAttributes(LinkedHashSet<String> rawAttributes)
292      {
293        if (rawAttributes == null)
294        {
295          this.rawAttributes = new LinkedHashSet<String>();
296        }
297        else
298        {
299          this.rawAttributes = rawAttributes;
300        }
301    
302        setValue(encodeAttributes(rawAttributes));
303        requestedAttributes = null;
304      }
305    
306    
307    
308      /**
309       * Retrieves the set of processed attributes that have been requested for
310       * inclusion in the entry that is returned.
311       *
312       * @return  The set of processed attributes that have been requested for
313       *          inclusion in the entry that is returned.
314       */
315      public LinkedHashSet<AttributeType> getRequestedAttributes()
316      {
317        if (requestedAttributes == null)
318        {
319          returnAllOperationalAttrs = false;
320          returnAllUserAttrs        = (rawAttributes.size() == 0);
321    
322          requestedAttributes =
323               new LinkedHashSet<AttributeType>(rawAttributes.size());
324          for (String attr : rawAttributes)
325          {
326            attr = attr.toLowerCase();
327    
328            if (attr.equals("*"))
329            {
330              returnAllUserAttrs = true;
331            }
332            else if (attr.equals("+"))
333            {
334              returnAllOperationalAttrs = true;
335            }
336            else if (attr.startsWith("@"))
337            {
338              String ocName = attr.substring(1);
339              ObjectClass oc = DirectoryServer.getObjectClass(ocName);
340              if (oc != null)
341              {
342                requestedAttributes.addAll(oc.getOptionalAttributeChain());
343                requestedAttributes.addAll(oc.getRequiredAttributeChain());
344              }
345            }
346            else
347            {
348              AttributeType at = DirectoryServer.getAttributeType(attr);
349              if (at == null)
350              {
351                at = DirectoryServer.getDefaultAttributeType(attr);
352              }
353    
354              requestedAttributes.add(at);
355            }
356          }
357        }
358    
359        return requestedAttributes;
360      }
361    
362    
363    
364      /**
365       * Indicates whether the entry returned should include all user attributes
366       * that the requester has permission to see.
367       *
368       * @return  <CODE>true</CODE> if the entry returned should include all user
369       *          attributes that the requester has permission to see, or
370       *          <CODE>false</CODE> if it should only include user attributes that
371       *          have been explicitly included in the requested attribute list.
372       */
373      public boolean returnAllUserAttributes()
374      {
375        if (requestedAttributes == null)
376        {
377          getRequestedAttributes();
378        }
379    
380        return returnAllUserAttrs;
381      }
382    
383    
384    
385      /**
386       * Indicates whether the entry returned should include all operational
387       * attributes that the requester has permission to see.
388       *
389       * @return  <CODE>true</CODE> if the entry returned should include all
390       *          operational attributes that the requester has permission to see,
391       *          or <CODE>false</CODE> if it should only include user attributes
392       *          that have been explicitly included in the requested attribute
393       *          list.
394       */
395      public boolean returnAllOperationalAttributes()
396      {
397        if (requestedAttributes == null)
398        {
399          getRequestedAttributes();
400        }
401    
402        return returnAllOperationalAttrs;
403      }
404    
405    
406    
407      /**
408       * Indicates whether the specified attribute type should be included in the
409       * entry for the corresponding response control.
410       *
411       * @param  attrType  The attribute type for which to make the determination.
412       *
413       * @return  <CODE>true</CODE> if the specified attribute type should be
414       *          included in the entry for the corresponding response control, or
415       *          <CODE>false</CODE> if not.
416       */
417      public boolean allowsAttribute(AttributeType attrType)
418      {
419        if (requestedAttributes == null)
420        {
421          getRequestedAttributes();
422        }
423    
424        if (requestedAttributes.contains(attrType))
425        {
426          return true;
427        }
428    
429        if (attrType.isOperational())
430        {
431          return returnAllOperationalAttrs;
432        }
433        else
434        {
435          return returnAllUserAttrs;
436        }
437      }
438    
439    
440    
441      /**
442       * Retrieves a string representation of this LDAP pre-read request control.
443       *
444       * @return  A string representation of this LDAP pre-read request control.
445       */
446      public String toString()
447      {
448        StringBuilder buffer = new StringBuilder();
449        toString(buffer);
450        return buffer.toString();
451      }
452    
453    
454    
455      /**
456       * Appends a string representation of this LDAP pre-read request control to
457       * the provided buffer.
458       *
459       * @param  buffer  The buffer to which the information should be appended.
460       */
461      public void toString(StringBuilder buffer)
462      {
463        buffer.append("LDAPPreReadRequestControl(criticality=");
464        buffer.append(isCritical());
465        buffer.append(",attrs=\"");
466    
467        if (! rawAttributes.isEmpty())
468        {
469          Iterator<String> iterator = rawAttributes.iterator();
470          buffer.append(iterator.next());
471    
472          while (iterator.hasNext())
473          {
474            buffer.append(",");
475            buffer.append(iterator.next());
476          }
477        }
478    
479        buffer.append("\")");
480      }
481    }
482