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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2007-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS 026 */ 027package org.opends.server.core; 028 029import java.util.ArrayList; 030import java.util.HashSet; 031import java.util.List; 032import java.util.Set; 033 034import org.forgerock.i18n.slf4j.LocalizedLogger; 035import org.forgerock.opendj.ldap.ByteString; 036import org.forgerock.opendj.ldap.ResultCode; 037import org.opends.server.api.ClientConnection; 038import org.opends.server.types.*; 039import org.opends.server.types.operation.PostResponseCompareOperation; 040import org.opends.server.types.operation.PreParseCompareOperation; 041import org.opends.server.workflowelement.localbackend.LocalBackendCompareOperation; 042 043import static org.opends.messages.CoreMessages.*; 044import static org.opends.server.core.DirectoryServer.*; 045import static org.opends.server.loggers.AccessLogger.*; 046import static org.opends.server.util.StaticUtils.*; 047import static org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement.*; 048 049/** 050 * This class defines an operation that may be used to determine whether a 051 * specified entry in the Directory Server contains a given attribute-value 052 * pair. 053 */ 054public class CompareOperationBasis 055 extends AbstractOperation 056 implements PreParseCompareOperation, CompareOperation, 057 PostResponseCompareOperation 058{ 059 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 060 061 /** The attribute type for this compare operation. */ 062 private AttributeType attributeType; 063 064 /** The assertion value for the compare operation. */ 065 private ByteString assertionValue; 066 067 /** The set of attribute options. */ 068 private Set<String> attributeOptions; 069 070 /** The raw, unprocessed entry DN as included in the client request. */ 071 private ByteString rawEntryDN; 072 073 /** The DN of the entry for the compare operation. */ 074 private DN entryDN; 075 076 /** The proxied authorization target DN for this operation. */ 077 private DN proxiedAuthorizationDN; 078 079 /** The set of response controls for this compare operation. */ 080 private List<Control> responseControls; 081 082 /** The attribute type for the compare operation. */ 083 private String rawAttributeType; 084 085 086 087 /** 088 * Creates a new compare operation with the provided information. 089 * 090 * @param clientConnection The client connection with which this operation 091 * is associated. 092 * @param operationID The operation ID for this operation. 093 * @param messageID The message ID of the request with which this 094 * operation is associated. 095 * @param requestControls The set of controls included in the request. 096 * @param rawEntryDN The raw, unprocessed entry DN as provided in the 097 * client request. This may or may not be a valid 098 * DN as no validation will have been performed yet. 099 * @param rawAttributeType The raw attribute type for the compare operation. 100 * @param assertionValue The assertion value for the compare operation. 101 */ 102 public CompareOperationBasis( 103 ClientConnection clientConnection, long operationID, 104 int messageID, List<Control> requestControls, 105 ByteString rawEntryDN, String rawAttributeType, 106 ByteString assertionValue) 107 { 108 super(clientConnection, operationID, messageID, requestControls); 109 110 111 this.rawEntryDN = rawEntryDN; 112 this.rawAttributeType = rawAttributeType; 113 this.assertionValue = assertionValue; 114 115 responseControls = new ArrayList<>(); 116 entryDN = null; 117 attributeType = null; 118 attributeOptions = null; 119 cancelRequest = null; 120 proxiedAuthorizationDN = null; 121 } 122 123 124 125 /** 126 * Creates a new compare operation with the provided information. 127 * 128 * @param clientConnection The client connection with which this operation 129 * is associated. 130 * @param operationID The operation ID for this operation. 131 * @param messageID The message ID of the request with which this 132 * operation is associated. 133 * @param requestControls The set of controls included in the request. 134 * @param entryDN The entry DN for this compare operation. 135 * @param attributeType The attribute type for this compare operation. 136 * @param assertionValue The assertion value for the compare operation. 137 */ 138 public CompareOperationBasis( 139 ClientConnection clientConnection, long operationID, 140 int messageID, List<Control> requestControls, 141 DN entryDN, AttributeType attributeType, 142 ByteString assertionValue) 143 { 144 super(clientConnection, operationID, messageID, requestControls); 145 146 147 this.entryDN = entryDN; 148 this.attributeType = attributeType; 149 this.assertionValue = assertionValue; 150 151 responseControls = new ArrayList<>(); 152 rawEntryDN = ByteString.valueOfUtf8(entryDN.toString()); 153 rawAttributeType = attributeType.getNameOrOID(); 154 cancelRequest = null; 155 proxiedAuthorizationDN = null; 156 attributeOptions = new HashSet<>(); 157 } 158 159 /** {@inheritDoc} */ 160 @Override 161 public final ByteString getRawEntryDN() 162 { 163 return rawEntryDN; 164 } 165 166 /** {@inheritDoc} */ 167 @Override 168 public final void setRawEntryDN(ByteString rawEntryDN) 169 { 170 this.rawEntryDN = rawEntryDN; 171 172 entryDN = null; 173 } 174 175 /** {@inheritDoc} */ 176 @Override 177 public final DN getEntryDN() 178 { 179 if (entryDN == null) { 180 try 181 { 182 entryDN = DN.decode(rawEntryDN); 183 } 184 catch (DirectoryException de) 185 { 186 logger.traceException(de); 187 188 setResultCode(de.getResultCode()); 189 appendErrorMessage(de.getMessageObject()); 190 } 191 } 192 return entryDN; 193 } 194 195 /** {@inheritDoc} */ 196 @Override 197 public final String getRawAttributeType() 198 { 199 return rawAttributeType; 200 } 201 202 /** {@inheritDoc} */ 203 @Override 204 public final void setRawAttributeType(String rawAttributeType) 205 { 206 this.rawAttributeType = rawAttributeType; 207 208 attributeType = null; 209 attributeOptions = null; 210 } 211 212 private void getAttributeTypeAndOptions() { 213 String baseName; 214 int semicolonPos = rawAttributeType.indexOf(';'); 215 if (semicolonPos > 0) { 216 baseName = toLowerCase(rawAttributeType.substring(0, semicolonPos)); 217 218 attributeOptions = new HashSet<>(); 219 int nextPos = rawAttributeType.indexOf(';', semicolonPos+1); 220 while (nextPos > 0) 221 { 222 attributeOptions.add( 223 rawAttributeType.substring(semicolonPos+1, nextPos)); 224 semicolonPos = nextPos; 225 nextPos = rawAttributeType.indexOf(';', semicolonPos+1); 226 } 227 228 attributeOptions.add(rawAttributeType.substring(semicolonPos+1)); 229 } 230 else 231 { 232 baseName = toLowerCase(rawAttributeType); 233 attributeOptions = null; 234 } 235 attributeType = DirectoryServer.getAttributeTypeOrDefault(baseName); 236 } 237 238 /** {@inheritDoc} */ 239 @Override 240 public final AttributeType getAttributeType() 241 { 242 if (attributeType == null) { 243 getAttributeTypeAndOptions(); 244 } 245 return attributeType; 246 } 247 248 /** {@inheritDoc} */ 249 @Override 250 public void setAttributeType(AttributeType attributeType) 251 { 252 this.attributeType = attributeType; 253 } 254 255 /** {@inheritDoc} */ 256 @Override 257 public Set<String> getAttributeOptions() 258 { 259 if (attributeOptions == null) { 260 getAttributeTypeAndOptions(); 261 } 262 return attributeOptions; 263 } 264 265 /** {@inheritDoc} */ 266 @Override 267 public void setAttributeOptions(Set<String> attributeOptions) 268 { 269 this.attributeOptions = attributeOptions; 270 } 271 272 /** {@inheritDoc} */ 273 @Override 274 public final ByteString getAssertionValue() 275 { 276 return assertionValue; 277 } 278 279 /** {@inheritDoc} */ 280 @Override 281 public final void setAssertionValue(ByteString assertionValue) 282 { 283 this.assertionValue = assertionValue; 284 } 285 286 /** {@inheritDoc} */ 287 @Override 288 public final OperationType getOperationType() 289 { 290 // Note that no debugging will be done in this method because it is a likely 291 // candidate for being called by the logging subsystem. 292 return OperationType.COMPARE; 293 } 294 295 296 297 /** 298 * Retrieves the proxied authorization DN for this operation if proxied 299 * authorization has been requested. 300 * 301 * @return The proxied authorization DN for this operation if proxied 302 * authorization has been requested, or {@code null} if proxied 303 * authorization has not been requested. 304 */ 305 @Override 306 public DN getProxiedAuthorizationDN() 307 { 308 return proxiedAuthorizationDN; 309 } 310 311 /** {@inheritDoc} */ 312 @Override 313 public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN) 314 { 315 this.proxiedAuthorizationDN = proxiedAuthorizationDN; 316 } 317 318 /** {@inheritDoc} */ 319 @Override 320 public final List<Control> getResponseControls() 321 { 322 return responseControls; 323 } 324 325 /** {@inheritDoc} */ 326 @Override 327 public final void addResponseControl(Control control) 328 { 329 responseControls.add(control); 330 } 331 332 /** {@inheritDoc} */ 333 @Override 334 public final void removeResponseControl(Control control) 335 { 336 responseControls.remove(control); 337 } 338 339 340 341 /** 342 * Performs the work of actually processing this operation. This 343 * should include all processing for the operation, including 344 * invoking plugins, logging messages, performing access control, 345 * managing synchronization, and any other work that might need to 346 * be done in the course of processing. 347 */ 348 @Override 349 public final void run() 350 { 351 setResultCode(ResultCode.UNDEFINED); 352 353 // Start the processing timer. 354 setProcessingStartTime(); 355 356 logCompareRequest(this); 357 358 // This flag is set to true as soon as a workflow has been executed. 359 boolean workflowExecuted = false; 360 try 361 { 362 // Check for and handle a request to cancel this operation. 363 checkIfCanceled(false); 364 365 // Invoke the pre-parse compare plugins. 366 if (!processOperationResult(getPluginConfigManager().invokePreParseComparePlugins(this))) 367 { 368 return; 369 } 370 371 372 // Check for a request to cancel this operation. 373 checkIfCanceled(false); 374 375 376 // Process the entry DN to convert it from the raw form to the form 377 // required for the rest of the compare processing. 378 try 379 { 380 if (entryDN == null) 381 { 382 entryDN = DN.decode(rawEntryDN); 383 } 384 } 385 catch (DirectoryException de) 386 { 387 logger.traceException(de); 388 389 setResultCode(de.getResultCode()); 390 appendErrorMessage(de.getMessageObject()); 391 392 return; 393 } 394 395 workflowExecuted = execute(this, entryDN); 396 } 397 catch(CanceledOperationException coe) 398 { 399 logger.traceException(coe); 400 401 setResultCode(ResultCode.CANCELLED); 402 cancelResult = new CancelResult(ResultCode.CANCELLED, null); 403 404 appendErrorMessage(coe.getCancelRequest().getCancelReason()); 405 } 406 finally 407 { 408 // Stop the processing timer. 409 setProcessingStopTime(); 410 411 // Log the compare response message. 412 logCompareResponse(this); 413 414 if(cancelRequest == null || cancelResult == null || 415 cancelResult.getResultCode() != ResultCode.CANCELLED || 416 cancelRequest.notifyOriginalRequestor() || 417 DirectoryServer.notifyAbandonedOperations()) 418 { 419 clientConnection.sendResponse(this); 420 } 421 422 // Invoke the post-response compare plugins. 423 invokePostResponsePlugins(workflowExecuted); 424 425 // If no cancel result, set it 426 if(cancelResult == null) 427 { 428 cancelResult = new CancelResult(ResultCode.TOO_LATE, null); 429 } 430 } 431 } 432 433 434 /** 435 * Invokes the post response plugins. If a workflow has been executed 436 * then invoke the post response plugins provided by the workflow 437 * elements of the workflow, otherwise invoke the post response plugins 438 * that have been registered with the current operation. 439 * 440 * @param workflowExecuted <code>true</code> if a workflow has been executed 441 */ 442 private void invokePostResponsePlugins(boolean workflowExecuted) 443 { 444 // Invoke the post response plugins 445 if (workflowExecuted) 446 { 447 // Invoke the post response plugins that have been registered by 448 // the workflow elements 449 List<LocalBackendCompareOperation> localOperations = 450 (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); 451 452 if (localOperations != null) 453 { 454 for (LocalBackendCompareOperation localOperation : localOperations) 455 { 456 getPluginConfigManager().invokePostResponseComparePlugins(localOperation); 457 } 458 } 459 } 460 else 461 { 462 // Invoke the post response plugins that have been registered with 463 // the current operation 464 getPluginConfigManager().invokePostResponseComparePlugins(this); 465 } 466 } 467 468 469 /** 470 * Updates the error message and the result code of the operation. 471 * 472 * This method is called because no workflow was found to process 473 * the operation. 474 */ 475 @Override 476 public void updateOperationErrMsgAndResCode() 477 { 478 setResultCode(ResultCode.NO_SUCH_OBJECT); 479 appendErrorMessage(ERR_COMPARE_NO_SUCH_ENTRY.get(getEntryDN())); 480 } 481 482 /** {@inheritDoc} */ 483 @Override 484 public final void toString(StringBuilder buffer) 485 { 486 buffer.append("CompareOperation(connID="); 487 buffer.append(clientConnection.getConnectionID()); 488 buffer.append(", opID="); 489 buffer.append(operationID); 490 buffer.append(", dn="); 491 buffer.append(rawEntryDN); 492 buffer.append(", attr="); 493 buffer.append(rawAttributeType); 494 buffer.append(")"); 495 } 496 497 498 /** 499 * {@inheritDoc} 500 * 501 * This method always returns null. 502 */ 503 @Override 504 public Entry getEntryToCompare() 505 { 506 return null; 507 } 508 509}