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