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