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 static org.opends.messages.CoreMessages.*;
030import static org.opends.server.core.DirectoryServer.*;
031import static org.opends.server.loggers.AccessLogger.*;
032import static org.opends.server.util.ServerConstants.*;
033
034import java.util.ArrayList;
035import java.util.Iterator;
036import java.util.List;
037
038import org.forgerock.i18n.slf4j.LocalizedLogger;
039import org.forgerock.opendj.ldap.ByteString;
040import org.forgerock.opendj.ldap.ResultCode;
041import org.opends.server.api.ClientConnection;
042import org.opends.server.api.ExtendedOperationHandler;
043import org.opends.server.types.*;
044import org.opends.server.types.operation.PostOperationExtendedOperation;
045import org.opends.server.types.operation.PostResponseExtendedOperation;
046import org.opends.server.types.operation.PreOperationExtendedOperation;
047import org.opends.server.types.operation.PreParseExtendedOperation;
048
049/**
050 * This class defines an extended operation, which can perform virtually any
051 * kind of task.
052 */
053public class ExtendedOperationBasis
054       extends AbstractOperation
055       implements ExtendedOperation,
056                  PreParseExtendedOperation,
057                  PreOperationExtendedOperation,
058                  PostOperationExtendedOperation,
059                  PostResponseExtendedOperation
060{
061  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
062
063  /** The value for the request associated with this extended operation. */
064  private ByteString requestValue;
065
066  /** The value for the response associated with this extended operation. */
067  private ByteString responseValue;
068
069  /** The set of response controls for this extended operation. */
070  private List<Control> responseControls;
071
072  /** The OID for the request associated with this extended operation. */
073  private String requestOID;
074
075  /** The OID for the response associated with this extended operation. */
076  private String responseOID;
077
078
079
080  /**
081   * Creates a new extended operation with the provided information.
082   *
083   * @param  clientConnection  The client connection with which this operation
084   *                           is associated.
085   * @param  operationID       The operation ID for this operation.
086   * @param  messageID         The message ID of the request with which this
087   *                           operation is associated.
088   * @param  requestControls   The set of controls included in the request.
089   * @param  requestOID        The OID for the request associated with this
090   *                           extended operation.
091   * @param  requestValue      The value for the request associated with this
092   *                           extended operation.
093   */
094  public ExtendedOperationBasis(ClientConnection clientConnection,
095                           long operationID,
096                           int messageID, List<Control> requestControls,
097                           String requestOID, ByteString requestValue)
098  {
099    super(clientConnection, operationID, messageID, requestControls);
100
101
102    this.requestOID   = requestOID;
103    this.requestValue = requestValue;
104
105    responseOID      = null;
106    responseValue    = null;
107    responseControls = new ArrayList<>();
108    cancelRequest    = null;
109
110    if (requestOID.equals(OID_CANCEL_REQUEST))
111    {
112      cancelResult = new CancelResult(ResultCode.CANNOT_CANCEL,
113          ERR_CANNOT_CANCEL_CANCEL.get());
114    }
115    if(requestOID.equals(OID_START_TLS_REQUEST))
116    {
117      cancelResult = new CancelResult(ResultCode.CANNOT_CANCEL,
118          ERR_CANNOT_CANCEL_START_TLS.get());
119    }
120  }
121
122
123
124  /** {@inheritDoc} */
125  @Override
126  public final String getRequestOID()
127  {
128    return requestOID;
129  }
130
131
132
133  /**
134   * Specifies the OID for the request associated with this extended operation.
135   * This should only be called by pre-parse plugins.
136   *
137   * @param  requestOID  The OID for the request associated with this extended
138   *                     operation.
139   */
140  @Override
141  public final void setRequestOID(String requestOID)
142  {
143    this.requestOID = requestOID;
144  }
145
146
147
148  /** {@inheritDoc} */
149  @Override
150  public DN getProxiedAuthorizationDN()
151  {
152    return null;
153  }
154
155
156
157  /** {@inheritDoc} */
158  @Override
159  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
160  {
161  }
162
163
164  /** {@inheritDoc} */
165  @Override
166  public final ByteString getRequestValue()
167  {
168    return requestValue;
169  }
170
171
172
173  /**
174   * Specifies the value for the request associated with this extended
175   * operation.  This should only be called by pre-parse plugins.
176   *
177   * @param  requestValue  The value for the request associated with this
178   *                       extended operation.
179   */
180  @Override
181  public final void setRequestValue(ByteString requestValue)
182  {
183    this.requestValue = requestValue;
184  }
185
186
187
188  /** {@inheritDoc} */
189  @Override
190  public final String getResponseOID()
191  {
192    return responseOID;
193  }
194
195
196
197  /** {@inheritDoc} */
198  @Override
199  public final void setResponseOID(String responseOID)
200  {
201    this.responseOID = responseOID;
202  }
203
204
205
206  /** {@inheritDoc} */
207  @Override
208  public final ByteString getResponseValue()
209  {
210    return responseValue;
211  }
212
213
214
215  /** {@inheritDoc} */
216  @Override
217  public final void setResponseValue(ByteString responseValue)
218  {
219    this.responseValue = responseValue;
220  }
221
222
223  /** {@inheritDoc} */
224  @Override
225  public final OperationType getOperationType()
226  {
227    // Note that no debugging will be done in this method because it is a likely
228    // candidate for being called by the logging subsystem.
229    return OperationType.EXTENDED;
230  }
231
232
233
234  /** {@inheritDoc} */
235  @Override
236  public final List<Control> getResponseControls()
237  {
238    return responseControls;
239  }
240
241
242
243  /** {@inheritDoc} */
244  @Override
245  public final void addResponseControl(Control control)
246  {
247    responseControls.add(control);
248  }
249
250
251
252  /** {@inheritDoc} */
253  @Override
254  public final void removeResponseControl(Control control)
255  {
256    responseControls.remove(control);
257  }
258
259
260
261  /**
262   * Performs the work of actually processing this operation.  This
263   * should include all processing for the operation, including
264   * invoking plugins, logging messages, performing access control,
265   * managing synchronization, and any other work that might need to
266   * be done in the course of processing.
267   */
268  @Override
269  public final void run()
270  {
271    setResultCode(ResultCode.UNDEFINED);
272
273    // Start the processing timer.
274    setProcessingStartTime();
275
276    logExtendedRequest(this);
277
278    try
279    {
280      // Check for and handle a request to cancel this operation.
281      checkIfCanceled(false);
282
283      // Invoke the pre-parse extended plugins.
284      if (!processOperationResult(getPluginConfigManager().invokePreParseExtendedPlugins(this)))
285      {
286        return;
287      }
288
289      checkIfCanceled(false);
290
291
292      // Get the extended operation handler for the request OID.  If there is
293      // none, then fail.
294      ExtendedOperationHandler<?> handler =
295           DirectoryServer.getExtendedOperationHandler(requestOID);
296      if (handler == null)
297      {
298        setResultCode(ResultCode.UNWILLING_TO_PERFORM);
299        appendErrorMessage(ERR_EXTENDED_NO_HANDLER.get(requestOID));
300        return;
301      }
302
303
304      // Look at the controls included in the request and ensure that all
305      // critical controls are supported by the handler.
306      List<Control> requestControls = getRequestControls();
307      if (requestControls != null && !requestControls.isEmpty())
308      {
309        for (Iterator<Control> iter = requestControls.iterator(); iter
310            .hasNext();)
311        {
312          final Control c = iter.next();
313          try
314          {
315            if (!AccessControlConfigManager.getInstance()
316                .getAccessControlHandler()
317                .isAllowed(getAuthorizationDN(), this, c))
318            {
319              // As per RFC 4511 4.1.11.
320              if (c.isCritical())
321              {
322                setResultCode(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
323                appendErrorMessage(ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS
324                    .get(c.getOID()));
325              }
326              else
327              {
328                // We don't want to process this non-critical control, so
329                // remove it.
330                iter.remove();
331                continue;
332              }
333            }
334          }
335          catch (DirectoryException e)
336          {
337            setResultCode(e.getResultCode());
338            appendErrorMessage(e.getMessageObject());
339            return;
340          }
341
342          if (! c.isCritical())
343          {
344            // The control isn't critical, so we don't care if it's supported
345            // or not.
346          }
347          else if (! handler.supportsControl(c.getOID()))
348          {
349            setResultCode(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
350            appendErrorMessage(ERR_EXTENDED_UNSUPPORTED_CRITICAL_CONTROL.get(requestOID, c.getOID()));
351            return;
352          }
353        }
354      }
355
356
357      // Check to see if the client has permission to perform the
358      // extended operation.
359
360      // FIXME: for now assume that this will check all permission
361      // pertinent to the operation. This includes proxy authorization
362      // and any other controls specified.
363      try
364      {
365        if (!AccessControlConfigManager.getInstance().getAccessControlHandler().isAllowed(this))
366        {
367          setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
368          appendErrorMessage(ERR_EXTENDED_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(requestOID));
369          return;
370        }
371      }
372      catch (DirectoryException e)
373      {
374        setResultCode(e.getResultCode());
375        appendErrorMessage(e.getMessageObject());
376        return;
377      }
378
379      try
380      {
381        // Invoke the pre-operation extended plugins.
382        if (!processOperationResult(getPluginConfigManager().invokePreOperationExtendedPlugins(this)))
383        {
384          return;
385        }
386
387        checkIfCanceled(false);
388
389        // Actually perform the processing for this operation.
390        handler.processExtendedOperation(this);
391
392      }
393      finally
394      {
395        getPluginConfigManager().invokePostOperationExtendedPlugins(this);
396      }
397
398    }
399    catch(CanceledOperationException coe)
400    {
401      logger.traceException(coe);
402
403      setResultCode(ResultCode.CANCELLED);
404      cancelResult = new CancelResult(ResultCode.CANCELLED, null);
405
406      appendErrorMessage(coe.getCancelRequest().getCancelReason());
407    }
408    finally
409    {
410      // Stop the processing timer.
411      setProcessingStopTime();
412
413      // Log the extended response.
414      logExtendedResponse(this);
415
416      // Send the response to the client.
417      if(cancelRequest == null || cancelResult == null ||
418          cancelResult.getResultCode() != ResultCode.CANCELLED ||
419          cancelRequest.notifyOriginalRequestor() ||
420          DirectoryServer.notifyAbandonedOperations())
421      {
422        clientConnection.sendResponse(this);
423      }
424
425      if(requestOID.equals(OID_START_TLS_REQUEST))
426      {
427        clientConnection.finishStartTLS();
428      }
429
430      // Invoke the post-response extended plugins.
431      getPluginConfigManager().invokePostResponseExtendedPlugins(this);
432
433      // If no cancel result, set it
434      if(cancelResult == null)
435      {
436        cancelResult = new CancelResult(ResultCode.TOO_LATE, null);
437      }
438    }
439  }
440
441  /** {@inheritDoc} */
442  @Override
443  public final void toString(StringBuilder buffer)
444  {
445    buffer.append("ExtendedOperation(connID=");
446    buffer.append(clientConnection.getConnectionID());
447    buffer.append(", opID=");
448    buffer.append(operationID);
449    buffer.append(", oid=");
450    buffer.append(requestOID);
451    buffer.append(")");
452  }
453
454}
455