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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.core; 028 import org.opends.messages.MessageBuilder; 029 030 031 import java.util.ArrayList; 032 import java.util.Iterator; 033 import java.util.List; 034 035 import org.opends.server.api.ClientConnection; 036 import org.opends.server.api.ExtendedOperationHandler; 037 import org.opends.server.api.plugin.PluginResult; 038 import org.opends.server.protocols.asn1.ASN1OctetString; 039 import org.opends.server.types.operation.PostOperationExtendedOperation; 040 import org.opends.server.types.operation.PostResponseExtendedOperation; 041 import org.opends.server.types.operation.PreOperationExtendedOperation; 042 import org.opends.server.types.operation.PreParseExtendedOperation; 043 044 import static org.opends.server.core.CoreConstants.*; 045 import static org.opends.server.loggers.AccessLogger.*; 046 import static org.opends.server.loggers.debug.DebugLogger.*; 047 048 import org.opends.server.loggers.debug.DebugLogger; 049 import org.opends.server.loggers.debug.DebugTracer; 050 import org.opends.server.types.*; 051 import static org.opends.messages.CoreMessages.*; 052 import static org.opends.server.util.ServerConstants.*; 053 054 055 056 /** 057 * This class defines an extended operation, which can perform virtually any 058 * kind of task. 059 */ 060 public class ExtendedOperationBasis 061 extends AbstractOperation 062 implements ExtendedOperation, 063 PreParseExtendedOperation, 064 PreOperationExtendedOperation, 065 PostOperationExtendedOperation, 066 PostResponseExtendedOperation 067 { 068 /** 069 * The tracer object for the debug logger. 070 */ 071 private static final DebugTracer TRACER = DebugLogger.getTracer(); 072 073 // The value for the request associated with this extended operation. 074 private ASN1OctetString requestValue; 075 076 // The value for the response associated with this extended operation. 077 private ASN1OctetString responseValue; 078 079 // Indicates whether a response has yet been sent for this operation. 080 private boolean responseSent; 081 082 // The set of response controls for this extended operation. 083 private List<Control> responseControls; 084 085 // The OID for the request associated with this extended operation. 086 private String requestOID; 087 088 // The OID for the response associated with this extended operation. 089 private String responseOID; 090 091 092 093 /** 094 * Creates a new extended operation with the provided information. 095 * 096 * @param clientConnection The client connection with which this operation 097 * is associated. 098 * @param operationID The operation ID for this operation. 099 * @param messageID The message ID of the request with which this 100 * operation is associated. 101 * @param requestControls The set of controls included in the request. 102 * @param requestOID The OID for the request associated with this 103 * extended operation. 104 * @param requestValue The value for the request associated with this 105 * extended operation. 106 */ 107 public ExtendedOperationBasis(ClientConnection clientConnection, 108 long operationID, 109 int messageID, List<Control> requestControls, 110 String requestOID, ASN1OctetString requestValue) 111 { 112 super(clientConnection, operationID, messageID, requestControls); 113 114 115 this.requestOID = requestOID; 116 this.requestValue = requestValue; 117 118 responseOID = null; 119 responseValue = null; 120 responseControls = new ArrayList<Control>(); 121 cancelRequest = null; 122 responseSent = false; 123 124 if (requestOID.equals(OID_CANCEL_REQUEST)) 125 { 126 cancelResult = new CancelResult(ResultCode.CANNOT_CANCEL, 127 ERR_CANNOT_CANCEL_CANCEL.get()); 128 } 129 if(requestOID.equals(OID_START_TLS_REQUEST)) 130 { 131 cancelResult = new CancelResult(ResultCode.CANNOT_CANCEL, 132 ERR_CANNOT_CANCEL_START_TLS.get()); 133 } 134 } 135 136 137 138 /** 139 * {@inheritDoc} 140 */ 141 public final String getRequestOID() 142 { 143 return requestOID; 144 } 145 146 147 148 /** 149 * Specifies the OID for the request associated with this extended operation. 150 * This should only be called by pre-parse plugins. 151 * 152 * @param requestOID The OID for the request associated with this extended 153 * operation. 154 */ 155 public final void setRequestOID(String requestOID) 156 { 157 this.requestOID = requestOID; 158 } 159 160 161 162 /** 163 * {@inheritDoc} 164 */ 165 public final ASN1OctetString getRequestValue() 166 { 167 return requestValue; 168 } 169 170 171 172 /** 173 * Specifies the value for the request associated with this extended 174 * operation. This should only be called by pre-parse plugins. 175 * 176 * @param requestValue The value for the request associated with this 177 * extended operation. 178 */ 179 public final void setRequestValue(ASN1OctetString requestValue) 180 { 181 this.requestValue = requestValue; 182 } 183 184 185 186 /** 187 * {@inheritDoc} 188 */ 189 public final String getResponseOID() 190 { 191 return responseOID; 192 } 193 194 195 196 /** 197 * {@inheritDoc} 198 */ 199 public final void setResponseOID(String responseOID) 200 { 201 this.responseOID = responseOID; 202 } 203 204 205 206 /** 207 * {@inheritDoc} 208 */ 209 public final ASN1OctetString getResponseValue() 210 { 211 return responseValue; 212 } 213 214 215 216 /** 217 * {@inheritDoc} 218 */ 219 public final void setResponseValue(ASN1OctetString responseValue) 220 { 221 this.responseValue = responseValue; 222 } 223 224 225 /** 226 * {@inheritDoc} 227 */ 228 @Override() 229 public final OperationType getOperationType() 230 { 231 // Note that no debugging will be done in this method because it is a likely 232 // candidate for being called by the logging subsystem. 233 234 return OperationType.EXTENDED; 235 } 236 237 238 239 /** 240 * {@inheritDoc} 241 */ 242 @Override() 243 public final String[][] getRequestLogElements() 244 { 245 // Note that no debugging will be done in this method because it is a likely 246 // candidate for being called by the logging subsystem. 247 248 return new String[][] 249 { 250 new String[] { LOG_ELEMENT_EXTENDED_REQUEST_OID, requestOID } 251 }; 252 } 253 254 255 256 /** 257 * {@inheritDoc} 258 */ 259 @Override() 260 public final String[][] getResponseLogElements() 261 { 262 // Note that no debugging will be done in this method because it is a likely 263 // candidate for being called by the logging subsystem. 264 265 String resultCode = String.valueOf(getResultCode().getIntValue()); 266 267 String errorMessage; 268 MessageBuilder errorMessageBuffer = getErrorMessage(); 269 if (errorMessageBuffer == null) 270 { 271 errorMessage = null; 272 } 273 else 274 { 275 errorMessage = errorMessageBuffer.toString(); 276 } 277 278 String matchedDNStr; 279 DN matchedDN = getMatchedDN(); 280 if (matchedDN == null) 281 { 282 matchedDNStr = null; 283 } 284 else 285 { 286 matchedDNStr = matchedDN.toString(); 287 } 288 289 String referrals; 290 List<String> referralURLs = getReferralURLs(); 291 if ((referralURLs == null) || referralURLs.isEmpty()) 292 { 293 referrals = null; 294 } 295 else 296 { 297 StringBuilder buffer = new StringBuilder(); 298 Iterator<String> iterator = referralURLs.iterator(); 299 buffer.append(iterator.next()); 300 301 while (iterator.hasNext()) 302 { 303 buffer.append(", "); 304 buffer.append(iterator.next()); 305 } 306 307 referrals = buffer.toString(); 308 } 309 310 String processingTime = 311 String.valueOf(getProcessingTime()); 312 313 return new String[][] 314 { 315 new String[] { LOG_ELEMENT_RESULT_CODE, resultCode }, 316 new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage }, 317 new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr }, 318 new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals }, 319 new String[] { LOG_ELEMENT_EXTENDED_RESPONSE_OID, responseOID }, 320 new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime } 321 }; 322 } 323 324 325 326 /** 327 * {@inheritDoc} 328 */ 329 @Override() 330 public final List<Control> getResponseControls() 331 { 332 return responseControls; 333 } 334 335 336 337 /** 338 * {@inheritDoc} 339 */ 340 @Override() 341 public final void addResponseControl(Control control) 342 { 343 responseControls.add(control); 344 } 345 346 347 348 /** 349 * {@inheritDoc} 350 */ 351 @Override() 352 public final void removeResponseControl(Control control) 353 { 354 responseControls.remove(control); 355 } 356 357 358 359 /** 360 * Performs the work of actually processing this operation. This 361 * should include all processing for the operation, including 362 * invoking plugins, logging messages, performing access control, 363 * managing synchronization, and any other work that might need to 364 * be done in the course of processing. 365 */ 366 public final void run() 367 { 368 setResultCode(ResultCode.UNDEFINED); 369 370 // Start the processing timer. 371 setProcessingStartTime(); 372 373 // Log the extended request message. 374 logExtendedRequest(this); 375 376 // Get the plugin config manager that will be used for invoking plugins. 377 PluginConfigManager pluginConfigManager = 378 DirectoryServer.getPluginConfigManager(); 379 380 try 381 { 382 // Check for and handle a request to cancel this operation. 383 checkIfCanceled(false); 384 385 // Invoke the pre-parse extended plugins. 386 PluginResult.PreParse preParseResult = 387 pluginConfigManager.invokePreParseExtendedPlugins(this); 388 389 if(!preParseResult.continueProcessing()) 390 { 391 setResultCode(preParseResult.getResultCode()); 392 appendErrorMessage(preParseResult.getErrorMessage()); 393 setMatchedDN(preParseResult.getMatchedDN()); 394 setReferralURLs(preParseResult.getReferralURLs()); 395 return; 396 } 397 398 checkIfCanceled(false); 399 400 401 // Get the extended operation handler for the request OID. If there is 402 // none, then fail. 403 ExtendedOperationHandler handler = 404 DirectoryServer.getExtendedOperationHandler(requestOID); 405 if (handler == null) 406 { 407 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 408 appendErrorMessage(ERR_EXTENDED_NO_HANDLER.get( 409 String.valueOf(requestOID))); 410 return; 411 } 412 413 414 // Look at the controls included in the request and ensure that all 415 // critical controls are supported by the handler. 416 List<Control> requestControls = getRequestControls(); 417 if ((requestControls != null) && (! requestControls.isEmpty())) 418 { 419 for (Control c : requestControls) 420 { 421 if (!AccessControlConfigManager.getInstance(). 422 getAccessControlHandler(). 423 isAllowed(this.getAuthorizationDN(), this, c)) { 424 setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); 425 426 appendErrorMessage(ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get( 427 c.getOID())); 428 return; 429 } 430 if (! c.isCritical()) 431 { 432 // The control isn't critical, so we don't care if it's supported 433 // or not. 434 } 435 else if (! handler.supportsControl(c.getOID())) 436 { 437 setResultCode(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); 438 439 appendErrorMessage(ERR_EXTENDED_UNSUPPORTED_CRITICAL_CONTROL.get( 440 String.valueOf(requestOID), 441 c.getOID())); 442 443 return; 444 } 445 } 446 } 447 448 449 // Check to see if the client has permission to perform the 450 // extended operation. 451 452 // FIXME: for now assume that this will check all permission 453 // pertinent to the operation. This includes proxy authorization 454 // and any other controls specified. 455 if (AccessControlConfigManager.getInstance() 456 .getAccessControlHandler().isAllowed(this) == false) { 457 setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); 458 459 appendErrorMessage(ERR_EXTENDED_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get( 460 String.valueOf(requestOID))); 461 462 return; 463 } 464 465 try 466 { 467 // Invoke the pre-operation extended plugins. 468 PluginResult.PreOperation preOpResult = 469 pluginConfigManager.invokePreOperationExtendedPlugins(this); 470 if(!preOpResult.continueProcessing()) 471 { 472 setResultCode(preParseResult.getResultCode()); 473 appendErrorMessage(preParseResult.getErrorMessage()); 474 setMatchedDN(preParseResult.getMatchedDN()); 475 setReferralURLs(preParseResult.getReferralURLs()); 476 return; 477 } 478 479 checkIfCanceled(false); 480 481 // Actually perform the processing for this operation. 482 handler.processExtendedOperation(this); 483 484 } 485 finally 486 { 487 pluginConfigManager.invokePostOperationExtendedPlugins(this); 488 } 489 490 } 491 catch(CanceledOperationException coe) 492 { 493 if (debugEnabled()) 494 { 495 TRACER.debugCaught(DebugLogLevel.ERROR, coe); 496 } 497 498 setResultCode(ResultCode.CANCELED); 499 cancelResult = new CancelResult(ResultCode.CANCELED, null); 500 501 appendErrorMessage(coe.getCancelRequest().getCancelReason()); 502 } 503 finally 504 { 505 // Stop the processing timer. 506 setProcessingStopTime(); 507 508 // Send the response to the client, if it has not already been sent. 509 if (! responseSent) 510 { 511 responseSent = true; 512 if(cancelRequest == null || cancelResult == null || 513 cancelResult.getResultCode() != ResultCode.CANCELED || 514 cancelRequest.notifyOriginalRequestor() || 515 DirectoryServer.notifyAbandonedOperations()) 516 { 517 clientConnection.sendResponse(this); 518 } 519 } 520 521 // Log the extended response. 522 logExtendedResponse(this); 523 524 // Invoke the post-response extended plugins. 525 pluginConfigManager.invokePostResponseExtendedPlugins(this); 526 527 // If no cancel result, set it 528 if(cancelResult == null) 529 { 530 cancelResult = new CancelResult(ResultCode.TOO_LATE, null); 531 } 532 } 533 } 534 535 536 537 /** 538 * Sends an extended response to the client if none has already been sent. 539 * Note that extended operation handlers are strongly discouraged from using 540 * this method when it is not necessary because its use will prevent the 541 * response from being sent after post-operation plugin processing, which may 542 * impact the result that should be included. Nevertheless, it may be needed 543 * in some special cases in which the response must be sent before the 544 * extended operation handler completes its processing (e.g., the StartTLS 545 * operation in which the response must be sent in the clear before actually 546 * enabling TLS protection). 547 */ 548 public final void sendExtendedResponse() 549 { 550 if (! responseSent) 551 { 552 responseSent = true; 553 clientConnection.sendResponse(this); 554 } 555 } 556 557 558 /** 559 * {@inheritDoc} 560 */ 561 public final void setResponseSent() 562 { 563 this.responseSent = true; 564 } 565 566 /** 567 * {@inheritDoc} 568 */ 569 @Override() 570 public final void toString(StringBuilder buffer) 571 { 572 buffer.append("ExtendedOperation(connID="); 573 buffer.append(clientConnection.getConnectionID()); 574 buffer.append(", opID="); 575 buffer.append(operationID); 576 buffer.append(", oid="); 577 buffer.append(requestOID); 578 buffer.append(")"); 579 } 580 581 } 582