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