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