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