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}