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 2006-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2011-2016 ForgeRock AS. 016 */ 017package org.opends.server.core; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import org.forgerock.i18n.LocalizedIllegalArgumentException; 023import org.forgerock.i18n.slf4j.LocalizedLogger; 024import org.forgerock.opendj.ldap.ByteString; 025import org.forgerock.opendj.ldap.DN; 026import org.forgerock.opendj.ldap.RDN; 027import org.forgerock.opendj.ldap.ResultCode; 028import org.opends.server.api.ClientConnection; 029import org.opends.server.types.AbstractOperation; 030import org.opends.server.types.CancelResult; 031import org.opends.server.types.CanceledOperationException; 032import org.opends.server.types.Control; 033import org.opends.server.types.Entry; 034import org.opends.server.types.Modification; 035import org.opends.server.types.Operation; 036import org.opends.server.types.OperationType; 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 @Override 206 public final DN getEntryDN() 207 { 208 if (entryDN == null) 209 { 210 entryDN = valueOfRawDN(rawEntryDN); 211 } 212 return entryDN; 213 } 214 215 private DN valueOfRawDN(ByteString dn) 216 { 217 try 218 { 219 return dn != null ? DN.valueOf(dn) : null; 220 } 221 catch (LocalizedIllegalArgumentException e) 222 { 223 logger.traceException(e); 224 setResultCode(ResultCode.INVALID_DN_SYNTAX); 225 appendErrorMessage(e.getMessageObject()); 226 return null; 227 } 228 } 229 230 @Override 231 public final ByteString getRawNewRDN() 232 { 233 return rawNewRDN; 234 } 235 236 /** {@inheritDoc} */ 237 @Override 238 public final void setRawNewRDN(ByteString rawNewRDN) 239 { 240 this.rawNewRDN = rawNewRDN; 241 242 newRDN = null; 243 newDN = null; 244 } 245 246 /** {@inheritDoc} */ 247 @Override 248 public final RDN getNewRDN() 249 { 250 try 251 { 252 if (newRDN == null) 253 { 254 newRDN = RDN.valueOf(rawNewRDN.toString()); 255 } 256 } 257 catch (LocalizedIllegalArgumentException e) 258 { 259 logger.traceException(e); 260 261 setResultCode(ResultCode.INVALID_DN_SYNTAX); 262 appendErrorMessage(e.getMessageObject()); 263 } 264 return newRDN; 265 } 266 267 /** {@inheritDoc} */ 268 @Override 269 public final boolean deleteOldRDN() 270 { 271 return deleteOldRDN; 272 } 273 274 /** {@inheritDoc} */ 275 @Override 276 public final void setDeleteOldRDN(boolean deleteOldRDN) 277 { 278 this.deleteOldRDN = deleteOldRDN; 279 } 280 281 /** {@inheritDoc} */ 282 @Override 283 public final ByteString getRawNewSuperior() 284 { 285 return rawNewSuperior; 286 } 287 288 /** {@inheritDoc} */ 289 @Override 290 public final void setRawNewSuperior(ByteString rawNewSuperior) 291 { 292 this.rawNewSuperior = rawNewSuperior; 293 294 newSuperior = null; 295 newDN = null; 296 } 297 298 @Override 299 public final DN getNewSuperior() 300 { 301 if (newSuperior == null) 302 { 303 newSuperior = valueOfRawDN(rawNewSuperior); 304 } 305 return newSuperior; 306 } 307 308 @Override 309 public final List<Modification> getModifications() 310 { 311 return modifications; 312 } 313 314 /** {@inheritDoc} */ 315 @Override 316 public final void addModification(Modification modification) 317 { 318 if (modifications == null) 319 { 320 modifications = new ArrayList<>(); 321 } 322 if (modification != null) 323 { 324 modifications.add(modification); 325 } 326 } 327 328 /** {@inheritDoc} */ 329 @Override 330 public final Entry getOriginalEntry() 331 { 332 return null; 333 } 334 335 /** {@inheritDoc} */ 336 @Override 337 public final Entry getUpdatedEntry() 338 { 339 return null; 340 } 341 342 /** {@inheritDoc} */ 343 @Override 344 public final OperationType getOperationType() 345 { 346 // Note that no debugging will be done in this method because it is a likely 347 // candidate for being called by the logging subsystem. 348 349 return OperationType.MODIFY_DN; 350 } 351 352 /** {@inheritDoc} */ 353 @Override 354 public DN getProxiedAuthorizationDN() 355 { 356 return proxiedAuthorizationDN; 357 } 358 359 /** {@inheritDoc} */ 360 @Override 361 public final List<Control> getResponseControls() 362 { 363 return responseControls; 364 } 365 366 /** {@inheritDoc} */ 367 @Override 368 public final void addResponseControl(Control control) 369 { 370 responseControls.add(control); 371 } 372 373 /** {@inheritDoc} */ 374 @Override 375 public final void removeResponseControl(Control control) 376 { 377 responseControls.remove(control); 378 } 379 380 381 /** 382 * Performs the work of actually processing this operation. This 383 * should include all processing for the operation, including 384 * invoking plugins, logging messages, performing access control, 385 * managing synchronization, and any other work that might need to 386 * be done in the course of processing. 387 */ 388 @Override 389 public final void run() 390 { 391 setResultCode(ResultCode.UNDEFINED); 392 393 // Start the processing timer. 394 setProcessingStartTime(); 395 396 logModifyDNRequest(this); 397 398 // This flag is set to true as soon as a workflow has been executed. 399 boolean workflowExecuted = false; 400 try 401 { 402 // Check for and handle a request to cancel this operation. 403 checkIfCanceled(false); 404 405 // Invoke the pre-parse modify DN plugins. 406 if (!processOperationResult(getPluginConfigManager().invokePreParseModifyDNPlugins(this))) 407 { 408 return; 409 } 410 411 // Check for and handle a request to cancel this operation. 412 checkIfCanceled(false); 413 414 // Process the entry DN, newRDN, and newSuperior elements from their raw 415 // forms as provided by the client to the forms required for the rest of 416 // the modify DN processing. 417 DN entryDN = getEntryDN(); 418 if (entryDN == null) 419 { 420 return; 421 } 422 423 workflowExecuted = execute(this, entryDN); 424 } 425 catch(CanceledOperationException coe) 426 { 427 logger.traceException(coe); 428 429 setResultCode(ResultCode.CANCELLED); 430 cancelResult = new CancelResult(ResultCode.CANCELLED, null); 431 432 appendErrorMessage(coe.getCancelRequest().getCancelReason()); 433 } 434 finally 435 { 436 // Stop the processing timer. 437 setProcessingStopTime(); 438 439 // Log the modify DN response. 440 logModifyDNResponse(this); 441 442 if(cancelRequest == null || cancelResult == null || 443 cancelResult.getResultCode() != ResultCode.CANCELLED || 444 cancelRequest.notifyOriginalRequestor() || 445 DirectoryServer.notifyAbandonedOperations()) 446 { 447 clientConnection.sendResponse(this); 448 } 449 450 // Invoke the post-response callbacks. 451 if (workflowExecuted) { 452 invokePostResponseCallbacks(); 453 } 454 455 // Invoke the post-response modify DN plugins. 456 invokePostResponsePlugins(workflowExecuted); 457 458 // If no cancel result, set it 459 if(cancelResult == null) 460 { 461 cancelResult = new CancelResult(ResultCode.TOO_LATE, null); 462 } 463 } 464 } 465 466 467 /** 468 * Invokes the post response plugins. If a workflow has been executed 469 * then invoke the post response plugins provided by the workflow 470 * elements of the workflow, otherwise invoke the post response plugins 471 * that have been registered with the current operation. 472 * 473 * @param workflowExecuted <code>true</code> if a workflow has been executed 474 */ 475 private void invokePostResponsePlugins(boolean workflowExecuted) 476 { 477 // Invoke the post response plugins 478 if (workflowExecuted) 479 { 480 // Invoke the post response plugins that have been registered by 481 // the workflow elements 482 @SuppressWarnings("unchecked") 483 List<LocalBackendModifyDNOperation> localOperations = 484 (List<LocalBackendModifyDNOperation>) 485 getAttachment(Operation.LOCALBACKENDOPERATIONS); 486 487 if (localOperations != null) 488 { 489 for (LocalBackendModifyDNOperation localOperation : localOperations) 490 { 491 getPluginConfigManager().invokePostResponseModifyDNPlugins(localOperation); 492 } 493 } 494 } 495 else 496 { 497 // Invoke the post response plugins that have been registered with 498 // the current operation 499 getPluginConfigManager().invokePostResponseModifyDNPlugins(this); 500 } 501 } 502 503 /** {@inheritDoc} */ 504 @Override 505 public void updateOperationErrMsgAndResCode() 506 { 507 setResultCode(ResultCode.NO_SUCH_OBJECT); 508 appendErrorMessage(ERR_MODDN_NO_BACKEND_FOR_CURRENT_ENTRY.get(entryDN)); 509 } 510 511 /** {@inheritDoc} */ 512 @Override 513 public final void toString(StringBuilder buffer) 514 { 515 buffer.append("ModifyDNOperation(connID="); 516 buffer.append(clientConnection.getConnectionID()); 517 buffer.append(", opID="); 518 buffer.append(operationID); 519 buffer.append(", dn="); 520 buffer.append(rawEntryDN); 521 buffer.append(", newRDN="); 522 buffer.append(rawNewRDN); 523 buffer.append(", deleteOldRDN="); 524 buffer.append(deleteOldRDN); 525 526 if (rawNewSuperior != null) 527 { 528 buffer.append(", newSuperior="); 529 buffer.append(rawNewSuperior); 530 } 531 buffer.append(")"); 532 } 533 534 /** {@inheritDoc} */ 535 @Override 536 public void setProxiedAuthorizationDN(DN dn) 537 { 538 proxiedAuthorizationDN = dn; 539 } 540 541 /** {@inheritDoc} */ 542 @Override 543 public DN getNewDN() 544 { 545 if (newDN == null) 546 { 547 // Construct the new DN to use for the entry. 548 DN parentDN = null; 549 if (getNewSuperior() == null) 550 { 551 if (getEntryDN() != null) 552 { 553 parentDN = DirectoryServer.getParentDNInSuffix(entryDN); 554 } 555 } 556 else 557 { 558 parentDN = newSuperior; 559 } 560 561 if (parentDN == null || parentDN.isRootDN()) 562 { 563 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 564 appendErrorMessage(ERR_MODDN_NO_PARENT.get(entryDN)); 565 } 566 newDN = parentDN.child(getNewRDN()); 567 } 568 return newDN; 569 } 570 571} 572