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