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.LDAPException;
045    import org.opends.server.types.ObjectClass;
046    
047    import static org.opends.server.loggers.debug.DebugLogger.*;
048    import org.opends.server.loggers.debug.DebugTracer;
049    import org.opends.server.types.DebugLogLevel;
050    import static org.opends.messages.ProtocolMessages.*;
051    import static org.opends.server.util.ServerConstants.*;
052    
053    
054    
055    /**
056     * This class implements the post-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 after an add, modify, or modify DN operation.  It may specify a
059     * specific set of attributes that should be included in that entry.  The entry
060     * will be encoded in a corresponding response control.
061     */
062    public class LDAPPostReadRequestControl
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 post-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 LDAPPostReadRequestControl(boolean isCritical,
100                                        LinkedHashSet<String> rawAttributes)
101      {
102        super(OID_LDAP_READENTRY_POSTREAD, 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 post-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 LDAPPostReadRequestControl(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 post-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 post-encoded value for this control.
166       */
167      private LDAPPostReadRequestControl(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 post-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 post-read request control.
196       *
197       * @return  The LDAP post-read request control decoded from the provided
198       *          control.
199       *
200       * @throws  LDAPException  If this control cannot be decoded as a valid LDAP
201       *                         post-read request control.
202       */
203      public static LDAPPostReadRequestControl decodeControl(Control control)
204             throws LDAPException
205      {
206        if (! control.hasValue())
207        {
208          Message message = ERR_POSTREADREQ_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 =
231              ERR_POSTREADREQ_CANNOT_DECODE_VALUE.get(ae.getMessage());
232          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message,
233                                  ae);
234        }
235    
236    
237        return new LDAPPostReadRequestControl(control.getOID(),
238                                              control.isCritical(),
239                                              rawAttributes, control.getValue());
240      }
241    
242    
243    
244      /**
245       * Encodes the provided set of raw, unprocessed attribute names to an
246       * ASN.1 octet string suitable for use as the value of this control.
247       *
248       * @param  rawAttributes  The set of attributes to encoded in the encoded
249       *                        control value.
250       *
251       * @return  The ASN.1 octet string containing the encoded attribute set.
252       */
253      private static ASN1OctetString encodeAttributes(LinkedHashSet<String>
254                                                           rawAttributes)
255      {
256        if (rawAttributes == null)
257        {
258          return new ASN1OctetString(new ASN1Sequence().encode());
259        }
260    
261        ArrayList<ASN1Element> elements =
262             new ArrayList<ASN1Element>(rawAttributes.size());
263        for (String attr : rawAttributes)
264        {
265          elements.add(new ASN1OctetString(attr));
266        }
267    
268        return new ASN1OctetString(new ASN1Sequence(elements).encode());
269      }
270    
271    
272    
273      /**
274       * Retrieves the raw, unprocessed set of requested attributes.  It must not
275       * be altered by the caller without calling <CODE>setRawAttributes</CODE> with
276       * the updated set.
277       *
278       * @return  The raw, unprocessed set of attributes.
279       */
280      public LinkedHashSet<String> getRawAttributes()
281      {
282        return rawAttributes;
283      }
284    
285    
286    
287      /**
288       * Specifies the raw, unprocessed set of requested attributes.  A null or
289       * empty set indicates that all user attributes should be returned.
290       *
291       * @param  rawAttributes  The raw, unprocessed set of requested attributes.
292       */
293      public void setRawAttributes(LinkedHashSet<String> rawAttributes)
294      {
295        if (rawAttributes == null)
296        {
297          this.rawAttributes = new LinkedHashSet<String>();
298        }
299        else
300        {
301          this.rawAttributes = rawAttributes;
302        }
303    
304        setValue(encodeAttributes(rawAttributes));
305        requestedAttributes = null;
306      }
307    
308    
309    
310      /**
311       * Retrieves the set of processed attributes that have been requested for
312       * inclusion in the entry that is returned.
313       *
314       * @return  The set of processed attributes that have been requested for
315       *          inclusion in the entry that is returned.
316       */
317      public LinkedHashSet<AttributeType> getRequestedAttributes()
318      {
319        if (requestedAttributes == null)
320        {
321          returnAllOperationalAttrs = false;
322          returnAllUserAttrs        = (rawAttributes.size() == 0);
323    
324          requestedAttributes =
325               new LinkedHashSet<AttributeType>(rawAttributes.size());
326          for (String attr : rawAttributes)
327          {
328            attr = attr.toLowerCase();
329    
330            if (attr.equals("*"))
331            {
332              returnAllUserAttrs = true;
333            }
334            else if (attr.equals("+"))
335            {
336              returnAllOperationalAttrs = true;
337            }
338            else if (attr.startsWith("@"))
339            {
340              String ocName = attr.substring(1);
341              ObjectClass oc = DirectoryServer.getObjectClass(ocName);
342              if (oc != null)
343              {
344                requestedAttributes.addAll(oc.getOptionalAttributeChain());
345                requestedAttributes.addAll(oc.getRequiredAttributeChain());
346              }
347            }
348            else
349            {
350              AttributeType at = DirectoryServer.getAttributeType(attr);
351              if (at == null)
352              {
353                at = DirectoryServer.getDefaultAttributeType(attr);
354              }
355    
356              requestedAttributes.add(at);
357            }
358          }
359        }
360    
361        return requestedAttributes;
362      }
363    
364    
365    
366      /**
367       * Indicates whether the entry returned should include all user attributes
368       * that the requester has permission to see.
369       *
370       * @return  <CODE>true</CODE> if the entry returned should include all user
371       *          attributes that the requester has permission to see, or
372       *          <CODE>false</CODE> if it should only include user attributes that
373       *          have been explicitly included in the requested attribute list.
374       */
375      public boolean returnAllUserAttributes()
376      {
377        if (requestedAttributes == null)
378        {
379          getRequestedAttributes();
380        }
381    
382        return returnAllUserAttrs;
383      }
384    
385    
386    
387      /**
388       * Indicates whether the entry returned should include all operational
389       * attributes that the requester has permission to see.
390       *
391       * @return  <CODE>true</CODE> if the entry returned should include all
392       *          operational attributes that the requester has permission to see,
393       *          or <CODE>false</CODE> if it should only include user attributes
394       *          that have been explicitly included in the requested attribute
395       *          list.
396       */
397      public boolean returnAllOperationalAttributes()
398      {
399        if (requestedAttributes == null)
400        {
401          getRequestedAttributes();
402        }
403    
404        return returnAllOperationalAttrs;
405      }
406    
407    
408    
409      /**
410       * Indicates whether the specified attribute type should be included in the
411       * entry for the corresponding response control.
412       *
413       * @param  attrType  The attribute type for which to make the determination.
414       *
415       * @return  <CODE>true</CODE> if the specified attribute type should be
416       *          included in the entry for the corresponding response control, or
417       *          <CODE>false</CODE> if not.
418       */
419      public boolean allowsAttribute(AttributeType attrType)
420      {
421        if (requestedAttributes == null)
422        {
423          getRequestedAttributes();
424        }
425    
426        if (requestedAttributes.contains(attrType))
427        {
428          return true;
429        }
430    
431        if (attrType.isOperational())
432        {
433          return returnAllOperationalAttrs;
434        }
435        else
436        {
437          return returnAllUserAttrs;
438        }
439      }
440    
441    
442    
443      /**
444       * Retrieves a string representation of this LDAP post-read request control.
445       *
446       * @return  A string representation of this LDAP post-read request control.
447       */
448      public String toString()
449      {
450        StringBuilder buffer = new StringBuilder();
451        toString(buffer);
452        return buffer.toString();
453      }
454    
455    
456    
457      /**
458       * Appends a string representation of this LDAP post-read request control to
459       * the provided buffer.
460       *
461       * @param  buffer  The buffer to which the information should be appended.
462       */
463      public void toString(StringBuilder buffer)
464      {
465        buffer.append("LDAPPostReadRequestControl(criticality=");
466        buffer.append(isCritical());
467        buffer.append(",attrs=\"");
468    
469        if (! rawAttributes.isEmpty())
470        {
471          Iterator<String> iterator = rawAttributes.iterator();
472          buffer.append(iterator.next());
473    
474          while (iterator.hasNext())
475          {
476            buffer.append(",");
477            buffer.append(iterator.next());
478          }
479        }
480    
481        buffer.append("\")");
482      }
483    }
484