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