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 2006-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS. 026 */ 027package org.opends.server.core; 028 029import java.util.ArrayList; 030import java.util.List; 031 032import org.forgerock.i18n.slf4j.LocalizedLogger; 033import org.forgerock.opendj.ldap.ByteString; 034import org.forgerock.opendj.ldap.ResultCode; 035import org.opends.server.api.ClientConnection; 036import org.opends.server.types.*; 037import org.opends.server.types.operation.PostResponseModifyDNOperation; 038import org.opends.server.types.operation.PreParseModifyDNOperation; 039import org.opends.server.workflowelement.localbackend.LocalBackendModifyDNOperation; 040 041import static org.opends.messages.CoreMessages.*; 042import static org.opends.server.core.DirectoryServer.*; 043import static org.opends.server.loggers.AccessLogger.*; 044import static org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement.*; 045 046/** 047 * This class defines an operation that may be used to alter the DN of an entry 048 * in the Directory Server. 049 */ 050public class ModifyDNOperationBasis 051 extends AbstractOperation 052 implements ModifyDNOperation, 053 PreParseModifyDNOperation, 054 PostResponseModifyDNOperation 055{ 056 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 057 058 /** Indicates whether to delete the old RDN value from the entry. */ 059 private boolean deleteOldRDN; 060 061 /** 062 * The raw, unprocessed current DN of the entry as included in the request 063 * from the client. 064 */ 065 private ByteString rawEntryDN; 066 067 /** The raw, unprocessed newRDN as included in the request from the client. */ 068 private ByteString rawNewRDN; 069 070 /** 071 * The raw, unprocessed newSuperior as included in the request from the 072 * client. 073 */ 074 private ByteString rawNewSuperior; 075 076 /** The current DN of the entry. */ 077 private DN entryDN; 078 079 /** The new parent for the entry. */ 080 private DN newSuperior; 081 082 /** The proxied authorization target DN for this operation. */ 083 private DN proxiedAuthorizationDN; 084 085 /** The set of response controls for this modify DN operation. */ 086 private List<Control> responseControls; 087 088 /** 089 * The set of modifications applied to attributes in the entry in the course 090 * of processing the modify DN. 091 */ 092 private List<Modification> modifications; 093 094 /** The new RDN for the entry. */ 095 private RDN newRDN; 096 097 /** The new entry DN. */ 098 private DN newDN; 099 100 /** 101 * Creates a new modify DN operation with the provided information. 102 * 103 * @param clientConnection The client connection with which this operation 104 * is associated. 105 * @param operationID The operation ID for this operation. 106 * @param messageID The message ID of the request with which this 107 * operation is associated. 108 * @param requestControls The set of controls included in the request. 109 * @param rawEntryDN The raw, unprocessed entry DN as included in the 110 * client request. 111 * @param rawNewRDN The raw, unprocessed newRDN as included in the 112 * client request. 113 * @param deleteOldRDN Indicates whether to delete the old RDN value 114 * from the entry. 115 * @param rawNewSuperior The raw, unprocessed newSuperior as included in 116 * the client request. 117 */ 118 public ModifyDNOperationBasis(ClientConnection clientConnection, 119 long operationID, 120 int messageID, List<Control> requestControls, 121 ByteString rawEntryDN, ByteString rawNewRDN, 122 boolean deleteOldRDN, ByteString rawNewSuperior) 123 { 124 super(clientConnection, operationID, messageID, requestControls); 125 126 127 this.rawEntryDN = rawEntryDN; 128 this.rawNewRDN = rawNewRDN; 129 this.deleteOldRDN = deleteOldRDN; 130 this.rawNewSuperior = rawNewSuperior; 131 132 entryDN = null; 133 newRDN = null; 134 newSuperior = null; 135 responseControls = new ArrayList<>(); 136 cancelRequest = null; 137 modifications = null; 138 } 139 140 141 142 /** 143 * Creates a new modify DN operation with the provided information. 144 * 145 * @param clientConnection The client connection with which this operation 146 * is associated. 147 * @param operationID The operation ID for this operation. 148 * @param messageID The message ID of the request with which this 149 * operation is associated. 150 * @param requestControls The set of controls included in the request. 151 * @param entryDN The current entry DN for this modify DN 152 * operation. 153 * @param newRDN The new RDN for this modify DN operation. 154 * @param deleteOldRDN Indicates whether to delete the old RDN value 155 * from the entry. 156 * @param newSuperior The newSuperior DN for this modify DN operation. 157 */ 158 public ModifyDNOperationBasis(ClientConnection clientConnection, 159 long operationID, 160 int messageID, List<Control> requestControls, 161 DN entryDN, RDN newRDN, boolean deleteOldRDN, 162 DN newSuperior) 163 { 164 super(clientConnection, operationID, messageID, requestControls); 165 166 167 this.entryDN = entryDN; 168 this.newRDN = newRDN; 169 this.deleteOldRDN = deleteOldRDN; 170 this.newSuperior = newSuperior; 171 172 rawEntryDN = ByteString.valueOfUtf8(entryDN.toString()); 173 rawNewRDN = ByteString.valueOfUtf8(newRDN.toString()); 174 175 if (newSuperior == null) 176 { 177 rawNewSuperior = null; 178 } 179 else 180 { 181 rawNewSuperior = ByteString.valueOfUtf8(newSuperior.toString()); 182 } 183 184 responseControls = new ArrayList<>(); 185 cancelRequest = null; 186 modifications = null; 187 } 188 189 /** {@inheritDoc} */ 190 @Override 191 public final ByteString getRawEntryDN() 192 { 193 return rawEntryDN; 194 } 195 196 /** {@inheritDoc} */ 197 @Override 198 public final void setRawEntryDN(ByteString rawEntryDN) 199 { 200 this.rawEntryDN = rawEntryDN; 201 202 entryDN = null; 203 } 204 205 /** {@inheritDoc} */ 206 @Override 207 public final DN getEntryDN() 208 { 209 try 210 { 211 if (entryDN == null) 212 { 213 entryDN = DN.decode(rawEntryDN); 214 } 215 } 216 catch (DirectoryException de) 217 { 218 logger.traceException(de); 219 setResultCode(de.getResultCode()); 220 appendErrorMessage(de.getMessageObject()); 221 } 222 return entryDN; 223 } 224 225 /** {@inheritDoc} */ 226 @Override 227 public final ByteString getRawNewRDN() 228 { 229 return rawNewRDN; 230 } 231 232 /** {@inheritDoc} */ 233 @Override 234 public final void setRawNewRDN(ByteString rawNewRDN) 235 { 236 this.rawNewRDN = rawNewRDN; 237 238 newRDN = null; 239 newDN = null; 240 } 241 242 /** {@inheritDoc} */ 243 @Override 244 public final RDN getNewRDN() 245 { 246 try 247 { 248 if (newRDN == null) 249 { 250 newRDN = RDN.decode(rawNewRDN.toString()); 251 } 252 } 253 catch (DirectoryException de) 254 { 255 logger.traceException(de); 256 257 setResultCode(de.getResultCode()); 258 appendErrorMessage(de.getMessageObject()); 259 } 260 return newRDN; 261 } 262 263 /** {@inheritDoc} */ 264 @Override 265 public final boolean deleteOldRDN() 266 { 267 return deleteOldRDN; 268 } 269 270 /** {@inheritDoc} */ 271 @Override 272 public final void setDeleteOldRDN(boolean deleteOldRDN) 273 { 274 this.deleteOldRDN = deleteOldRDN; 275 } 276 277 /** {@inheritDoc} */ 278 @Override 279 public final ByteString getRawNewSuperior() 280 { 281 return rawNewSuperior; 282 } 283 284 /** {@inheritDoc} */ 285 @Override 286 public final void setRawNewSuperior(ByteString rawNewSuperior) 287 { 288 this.rawNewSuperior = rawNewSuperior; 289 290 newSuperior = null; 291 newDN = null; 292 } 293 294 /** {@inheritDoc} */ 295 @Override 296 public final DN getNewSuperior() 297 { 298 if (rawNewSuperior == null) 299 { 300 newSuperior = null; 301 } 302 else 303 { 304 try 305 { 306 if (newSuperior == null) 307 { 308 newSuperior = DN.decode(rawNewSuperior); 309 } 310 } 311 catch (DirectoryException de) 312 { 313 logger.traceException(de); 314 315 setResultCode(de.getResultCode()); 316 appendErrorMessage(de.getMessageObject()); 317 } 318 } 319 return newSuperior; 320 } 321 322 /** {@inheritDoc} */ 323 @Override 324 public final List<Modification> getModifications() 325 { 326 return modifications; 327 } 328 329 /** {@inheritDoc} */ 330 @Override 331 public final void addModification(Modification modification) 332 { 333 if (modifications == null) 334 { 335 modifications = new ArrayList<>(); 336 } 337 if (modification != null) 338 { 339 modifications.add(modification); 340 } 341 } 342 343 /** {@inheritDoc} */ 344 @Override 345 public final Entry getOriginalEntry() 346 { 347 return null; 348 } 349 350 /** {@inheritDoc} */ 351 @Override 352 public final Entry getUpdatedEntry() 353 { 354 return null; 355 } 356 357 /** {@inheritDoc} */ 358 @Override 359 public final OperationType getOperationType() 360 { 361 // Note that no debugging will be done in this method because it is a likely 362 // candidate for being called by the logging subsystem. 363 364 return OperationType.MODIFY_DN; 365 } 366 367 /** {@inheritDoc} */ 368 @Override 369 public DN getProxiedAuthorizationDN() 370 { 371 return proxiedAuthorizationDN; 372 } 373 374 /** {@inheritDoc} */ 375 @Override 376 public final List<Control> getResponseControls() 377 { 378 return responseControls; 379 } 380 381 /** {@inheritDoc} */ 382 @Override 383 public final void addResponseControl(Control control) 384 { 385 responseControls.add(control); 386 } 387 388 /** {@inheritDoc} */ 389 @Override 390 public final void removeResponseControl(Control control) 391 { 392 responseControls.remove(control); 393 } 394 395 396 /** 397 * Performs the work of actually processing this operation. This 398 * should include all processing for the operation, including 399 * invoking plugins, logging messages, performing access control, 400 * managing synchronization, and any other work that might need to 401 * be done in the course of processing. 402 */ 403 @Override 404 public final void run() 405 { 406 setResultCode(ResultCode.UNDEFINED); 407 408 // Start the processing timer. 409 setProcessingStartTime(); 410 411 logModifyDNRequest(this); 412 413 // This flag is set to true as soon as a workflow has been executed. 414 boolean workflowExecuted = false; 415 try 416 { 417 // Check for and handle a request to cancel this operation. 418 checkIfCanceled(false); 419 420 // Invoke the pre-parse modify DN plugins. 421 if (!processOperationResult(getPluginConfigManager().invokePreParseModifyDNPlugins(this))) 422 { 423 return; 424 } 425 426 // Check for and handle a request to cancel this operation. 427 checkIfCanceled(false); 428 429 // Process the entry DN, newRDN, and newSuperior elements from their raw 430 // forms as provided by the client to the forms required for the rest of 431 // the modify DN processing. 432 DN entryDN = getEntryDN(); 433 if (entryDN == null) 434 { 435 return; 436 } 437 438 workflowExecuted = execute(this, entryDN); 439 } 440 catch(CanceledOperationException coe) 441 { 442 logger.traceException(coe); 443 444 setResultCode(ResultCode.CANCELLED); 445 cancelResult = new CancelResult(ResultCode.CANCELLED, null); 446 447 appendErrorMessage(coe.getCancelRequest().getCancelReason()); 448 } 449 finally 450 { 451 // Stop the processing timer. 452 setProcessingStopTime(); 453 454 // Log the modify DN response. 455 logModifyDNResponse(this); 456 457 if(cancelRequest == null || cancelResult == null || 458 cancelResult.getResultCode() != ResultCode.CANCELLED || 459 cancelRequest.notifyOriginalRequestor() || 460 DirectoryServer.notifyAbandonedOperations()) 461 { 462 clientConnection.sendResponse(this); 463 } 464 465 // Invoke the post-response callbacks. 466 if (workflowExecuted) { 467 invokePostResponseCallbacks(); 468 } 469 470 // Invoke the post-response modify DN plugins. 471 invokePostResponsePlugins(workflowExecuted); 472 473 // If no cancel result, set it 474 if(cancelResult == null) 475 { 476 cancelResult = new CancelResult(ResultCode.TOO_LATE, null); 477 } 478 } 479 } 480 481 482 /** 483 * Invokes the post response plugins. If a workflow has been executed 484 * then invoke the post response plugins provided by the workflow 485 * elements of the workflow, otherwise invoke the post response plugins 486 * that have been registered with the current operation. 487 * 488 * @param workflowExecuted <code>true</code> if a workflow has been executed 489 */ 490 private void invokePostResponsePlugins(boolean workflowExecuted) 491 { 492 // Invoke the post response plugins 493 if (workflowExecuted) 494 { 495 // Invoke the post response plugins that have been registered by 496 // the workflow elements 497 @SuppressWarnings("unchecked") 498 List<LocalBackendModifyDNOperation> localOperations = 499 (List<LocalBackendModifyDNOperation>) 500 getAttachment(Operation.LOCALBACKENDOPERATIONS); 501 502 if (localOperations != null) 503 { 504 for (LocalBackendModifyDNOperation localOperation : localOperations) 505 { 506 getPluginConfigManager().invokePostResponseModifyDNPlugins(localOperation); 507 } 508 } 509 } 510 else 511 { 512 // Invoke the post response plugins that have been registered with 513 // the current operation 514 getPluginConfigManager().invokePostResponseModifyDNPlugins(this); 515 } 516 } 517 518 /** {@inheritDoc} */ 519 @Override 520 public void updateOperationErrMsgAndResCode() 521 { 522 setResultCode(ResultCode.NO_SUCH_OBJECT); 523 appendErrorMessage(ERR_MODDN_NO_BACKEND_FOR_CURRENT_ENTRY.get(entryDN)); 524 } 525 526 /** {@inheritDoc} */ 527 @Override 528 public final void toString(StringBuilder buffer) 529 { 530 buffer.append("ModifyDNOperation(connID="); 531 buffer.append(clientConnection.getConnectionID()); 532 buffer.append(", opID="); 533 buffer.append(operationID); 534 buffer.append(", dn="); 535 buffer.append(rawEntryDN); 536 buffer.append(", newRDN="); 537 buffer.append(rawNewRDN); 538 buffer.append(", deleteOldRDN="); 539 buffer.append(deleteOldRDN); 540 541 if (rawNewSuperior != null) 542 { 543 buffer.append(", newSuperior="); 544 buffer.append(rawNewSuperior); 545 } 546 buffer.append(")"); 547 } 548 549 /** {@inheritDoc} */ 550 @Override 551 public void setProxiedAuthorizationDN(DN dn) 552 { 553 proxiedAuthorizationDN = dn; 554 } 555 556 /** {@inheritDoc} */ 557 @Override 558 public DN getNewDN() 559 { 560 if (newDN == null) 561 { 562 // Construct the new DN to use for the entry. 563 DN parentDN = null; 564 if (getNewSuperior() == null) 565 { 566 if (getEntryDN() != null) 567 { 568 parentDN = entryDN.getParentDNInSuffix(); 569 } 570 } 571 else 572 { 573 parentDN = newSuperior; 574 } 575 576 if (parentDN == null || parentDN.isRootDN()) 577 { 578 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 579 appendErrorMessage(ERR_MODDN_NO_PARENT.get(entryDN)); 580 } 581 newDN = parentDN.child(getNewRDN()); 582 } 583 return newDN; 584 } 585 586} 587