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