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