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 2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.workflowelement.localbackend; 028 029 030 031 import java.util.List; 032 033 import org.opends.server.api.Backend; 034 import org.opends.server.api.ClientConnection; 035 import org.opends.server.api.plugin.PluginResult; 036 import org.opends.server.controls.LDAPAssertionRequestControl; 037 import org.opends.server.controls.MatchedValuesControl; 038 import org.opends.server.controls.PersistentSearchControl; 039 import org.opends.server.controls.ProxiedAuthV1Control; 040 import org.opends.server.controls.ProxiedAuthV2Control; 041 import org.opends.server.core.AccessControlConfigManager; 042 import org.opends.server.core.DirectoryServer; 043 import org.opends.server.core.PersistentSearch; 044 import org.opends.server.core.PluginConfigManager; 045 import org.opends.server.core.SearchOperationWrapper; 046 import org.opends.server.core.SearchOperation; 047 import org.opends.server.loggers.debug.DebugTracer; 048 import org.opends.server.types.CanceledOperationException; 049 import org.opends.server.types.Control; 050 import org.opends.server.types.DebugLogLevel; 051 import org.opends.server.types.DirectoryException; 052 import org.opends.server.types.DN; 053 import org.opends.server.types.Entry; 054 import org.opends.server.types.LDAPException; 055 import org.opends.server.types.Privilege; 056 import org.opends.server.types.ResultCode; 057 import org.opends.server.types.SearchFilter; 058 import org.opends.server.types.operation.PostOperationSearchOperation; 059 import org.opends.server.types.operation.PreOperationSearchOperation; 060 import org.opends.server.types.operation.SearchEntrySearchOperation; 061 import org.opends.server.types.operation.SearchReferenceSearchOperation; 062 063 import static org.opends.messages.CoreMessages.*; 064 import static org.opends.server.loggers.debug.DebugLogger.*; 065 import static org.opends.server.util.ServerConstants.*; 066 import static org.opends.server.util.StaticUtils.*; 067 068 069 070 /** 071 * This class defines an operation used to search for entries in a local backend 072 * of the Directory Server. 073 */ 074 public class LocalBackendSearchOperation 075 extends SearchOperationWrapper 076 implements PreOperationSearchOperation, PostOperationSearchOperation, 077 SearchEntrySearchOperation, SearchReferenceSearchOperation 078 { 079 /** 080 * The tracer object for the debug logger. 081 */ 082 private static final DebugTracer TRACER = getTracer(); 083 084 085 086 // The backend in which the search is to be performed. 087 private Backend backend; 088 089 // Indicates whether we should actually process the search. This should 090 // only be false if it's a persistent search with changesOnly=true. 091 private boolean processSearch; 092 093 // The client connection for the search operation. 094 private ClientConnection clientConnection; 095 096 // The base DN for the search. 097 private DN baseDN; 098 099 // The persistent search request, if applicable. 100 private PersistentSearch persistentSearch; 101 102 // The filter for the search. 103 private SearchFilter filter; 104 105 106 107 /** 108 * Creates a new operation that may be used to search for entries in a local 109 * backend of the Directory Server. 110 * 111 * @param search The operation to process. 112 */ 113 public LocalBackendSearchOperation(SearchOperation search) 114 { 115 super(search); 116 LocalBackendWorkflowElement.attachLocalOperation(search, this); 117 } 118 119 120 121 /** 122 * Process this search operation against a local backend. 123 * 124 * @param backend The backend in which the search operation should be 125 * performed. 126 * 127 * @throws CanceledOperationException if this operation should be 128 * cancelled 129 */ 130 void processLocalSearch(Backend backend) throws CanceledOperationException { 131 boolean executePostOpPlugins = false; 132 133 this.backend = backend; 134 135 clientConnection = getClientConnection(); 136 137 // Get the plugin config manager that will be used for invoking plugins. 138 PluginConfigManager pluginConfigManager = 139 DirectoryServer.getPluginConfigManager(); 140 processSearch = true; 141 142 // Check for a request to cancel this operation. 143 checkIfCanceled(false); 144 145 // Create a labeled block of code that we can break out of if a problem is 146 // detected. 147 searchProcessing: 148 { 149 // Process the search base and filter to convert them from their raw forms 150 // as provided by the client to the forms required for the rest of the 151 // search processing. 152 baseDN = getBaseDN(); 153 filter = getFilter(); 154 155 if ((baseDN == null) || (filter == null)){ 156 break searchProcessing; 157 } 158 159 // Check to see if there are any controls in the request. If so, then 160 // see if there is any special processing required. 161 try 162 { 163 handleRequestControls(); 164 } 165 catch (DirectoryException de) 166 { 167 if (debugEnabled()) 168 { 169 TRACER.debugCaught(DebugLogLevel.ERROR, de); 170 } 171 172 setResponseData(de); 173 break searchProcessing; 174 } 175 176 177 // Check to see if the client has permission to perform the 178 // search. 179 180 // FIXME: for now assume that this will check all permission 181 // pertinent to the operation. This includes proxy authorization 182 // and any other controls specified. 183 if (! AccessControlConfigManager.getInstance().getAccessControlHandler(). 184 isAllowed(this)) 185 { 186 setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); 187 appendErrorMessage(ERR_SEARCH_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get( 188 String.valueOf(baseDN))); 189 break searchProcessing; 190 } 191 192 // Check for a request to cancel this operation. 193 checkIfCanceled(false); 194 195 196 // Invoke the pre-operation search plugins. 197 executePostOpPlugins = true; 198 PluginResult.PreOperation preOpResult = 199 pluginConfigManager.invokePreOperationSearchPlugins(this); 200 if (!preOpResult.continueProcessing()) 201 { 202 setResultCode(preOpResult.getResultCode()); 203 appendErrorMessage(preOpResult.getErrorMessage()); 204 setMatchedDN(preOpResult.getMatchedDN()); 205 setReferralURLs(preOpResult.getReferralURLs()); 206 break searchProcessing; 207 } 208 209 210 // Check for a request to cancel this operation. 211 checkIfCanceled(false); 212 213 214 // Get the backend that should hold the search base. If there is none, 215 // then fail. 216 if (backend == null) 217 { 218 setResultCode(ResultCode.NO_SUCH_OBJECT); 219 appendErrorMessage(ERR_SEARCH_BASE_DOESNT_EXIST.get( 220 String.valueOf(baseDN))); 221 break searchProcessing; 222 } 223 224 225 // We'll set the result code to "success". If a problem occurs, then it 226 // will be overwritten. 227 setResultCode(ResultCode.SUCCESS); 228 229 230 // If there's a persistent search, then register it with the server. 231 if (persistentSearch != null) 232 { 233 DirectoryServer.registerPersistentSearch(persistentSearch); 234 setSendResponse(false); 235 } 236 237 238 // Process the search in the backend and all its subordinates. 239 try 240 { 241 if (processSearch) 242 { 243 backend.search(this); 244 } 245 } 246 catch (DirectoryException de) 247 { 248 if (debugEnabled()) 249 { 250 TRACER.debugCaught(DebugLogLevel.VERBOSE, de); 251 } 252 253 setResponseData(de); 254 255 if (persistentSearch != null) 256 { 257 DirectoryServer.deregisterPersistentSearch(persistentSearch); 258 setSendResponse(true); 259 } 260 261 break searchProcessing; 262 } 263 catch (Exception e) 264 { 265 if (debugEnabled()) 266 { 267 TRACER.debugCaught(DebugLogLevel.ERROR, e); 268 } 269 270 setResultCode(DirectoryServer.getServerErrorResultCode()); 271 appendErrorMessage(ERR_SEARCH_BACKEND_EXCEPTION.get( 272 getExceptionMessage(e))); 273 274 if (persistentSearch != null) 275 { 276 DirectoryServer.deregisterPersistentSearch(persistentSearch); 277 setSendResponse(true); 278 } 279 280 break searchProcessing; 281 } 282 } 283 284 285 // Check for a request to cancel this operation. 286 checkIfCanceled(false); 287 288 // Invoke the post-operation search plugins. 289 if (executePostOpPlugins) 290 { 291 PluginResult.PostOperation postOpResult = 292 pluginConfigManager.invokePostOperationSearchPlugins(this); 293 if (!postOpResult.continueProcessing()) 294 { 295 setResultCode(postOpResult.getResultCode()); 296 appendErrorMessage(postOpResult.getErrorMessage()); 297 setMatchedDN(postOpResult.getMatchedDN()); 298 setReferralURLs(postOpResult.getReferralURLs()); 299 } 300 } 301 } 302 303 304 /** 305 * Handles any controls contained in the request. 306 * 307 * @throws DirectoryException If there is a problem with any of the request 308 * controls. 309 */ 310 private void handleRequestControls() 311 throws DirectoryException 312 { 313 List<Control> requestControls = getRequestControls(); 314 if ((requestControls != null) && (! requestControls.isEmpty())) 315 { 316 for (int i=0; i < requestControls.size(); i++) 317 { 318 Control c = requestControls.get(i); 319 String oid = c.getOID(); 320 if (! AccessControlConfigManager.getInstance(). 321 getAccessControlHandler().isAllowed(baseDN, this, c)) 322 { 323 throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 324 ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid)); 325 } 326 327 if (oid.equals(OID_LDAP_ASSERTION)) 328 { 329 LDAPAssertionRequestControl assertControl; 330 if (c instanceof LDAPAssertionRequestControl) 331 { 332 assertControl = (LDAPAssertionRequestControl) c; 333 } 334 else 335 { 336 try 337 { 338 assertControl = LDAPAssertionRequestControl.decodeControl(c); 339 requestControls.set(i, assertControl); 340 } 341 catch (LDAPException le) 342 { 343 if (debugEnabled()) 344 { 345 TRACER.debugCaught(DebugLogLevel.ERROR, le); 346 } 347 348 throw new DirectoryException( 349 ResultCode.valueOf(le.getResultCode()), 350 le.getMessageObject(), le); 351 } 352 } 353 354 try 355 { 356 // FIXME -- We need to determine whether the current user has 357 // permission to make this determination. 358 SearchFilter assertionFilter = assertControl.getSearchFilter(); 359 Entry entry; 360 try 361 { 362 entry = DirectoryServer.getEntry(baseDN); 363 } 364 catch (DirectoryException de) 365 { 366 if (debugEnabled()) 367 { 368 TRACER.debugCaught(DebugLogLevel.ERROR, de); 369 } 370 371 throw new DirectoryException(de.getResultCode(), 372 ERR_SEARCH_CANNOT_GET_ENTRY_FOR_ASSERTION.get( 373 de.getMessageObject())); 374 } 375 376 if (entry == null) 377 { 378 throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, 379 ERR_SEARCH_NO_SUCH_ENTRY_FOR_ASSERTION.get()); 380 } 381 382 if (! assertionFilter.matchesEntry(entry)) 383 { 384 throw new DirectoryException(ResultCode.ASSERTION_FAILED, 385 ERR_SEARCH_ASSERTION_FAILED.get()); 386 } 387 } 388 catch (DirectoryException de) 389 { 390 if (de.getResultCode() == ResultCode.ASSERTION_FAILED) 391 { 392 throw de; 393 } 394 395 if (debugEnabled()) 396 { 397 TRACER.debugCaught(DebugLogLevel.ERROR, de); 398 } 399 400 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 401 ERR_SEARCH_CANNOT_PROCESS_ASSERTION_FILTER.get( 402 de.getMessageObject()), de); 403 } 404 } 405 else if (oid.equals(OID_PROXIED_AUTH_V1)) 406 { 407 // The requester must have the PROXIED_AUTH privilige in order to be 408 // able to use this control. 409 if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) 410 { 411 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, 412 ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get()); 413 } 414 415 416 ProxiedAuthV1Control proxyControl; 417 if (c instanceof ProxiedAuthV1Control) 418 { 419 proxyControl = (ProxiedAuthV1Control) c; 420 } 421 else 422 { 423 try 424 { 425 proxyControl = ProxiedAuthV1Control.decodeControl(c); 426 } 427 catch (LDAPException le) 428 { 429 if (debugEnabled()) 430 { 431 TRACER.debugCaught(DebugLogLevel.ERROR, le); 432 } 433 434 throw new DirectoryException( 435 ResultCode.valueOf(le.getResultCode()), 436 le.getMessageObject(), le); 437 } 438 } 439 440 441 Entry authorizationEntry = proxyControl.getAuthorizationEntry(); 442 setAuthorizationEntry(authorizationEntry); 443 if (authorizationEntry == null) 444 { 445 setProxiedAuthorizationDN(DN.nullDN()); 446 } 447 else 448 { 449 setProxiedAuthorizationDN(authorizationEntry.getDN()); 450 } 451 } 452 else if (oid.equals(OID_PROXIED_AUTH_V2)) 453 { 454 // The requester must have the PROXIED_AUTH privilige in order to be 455 // able to use this control. 456 if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) 457 { 458 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, 459 ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get()); 460 } 461 462 463 ProxiedAuthV2Control proxyControl; 464 if (c instanceof ProxiedAuthV2Control) 465 { 466 proxyControl = (ProxiedAuthV2Control) c; 467 } 468 else 469 { 470 try 471 { 472 proxyControl = ProxiedAuthV2Control.decodeControl(c); 473 } 474 catch (LDAPException le) 475 { 476 if (debugEnabled()) 477 { 478 TRACER.debugCaught(DebugLogLevel.ERROR, le); 479 } 480 481 throw new DirectoryException( 482 ResultCode.valueOf(le.getResultCode()), 483 le.getMessageObject(), le); 484 } 485 } 486 487 488 Entry authorizationEntry = proxyControl.getAuthorizationEntry(); 489 setAuthorizationEntry(authorizationEntry); 490 if (authorizationEntry == null) 491 { 492 setProxiedAuthorizationDN(DN.nullDN()); 493 } 494 else 495 { 496 setProxiedAuthorizationDN(authorizationEntry.getDN()); 497 } 498 } 499 else if (oid.equals(OID_PERSISTENT_SEARCH)) 500 { 501 PersistentSearchControl psearchControl; 502 if (c instanceof PersistentSearchControl) 503 { 504 psearchControl = (PersistentSearchControl) c; 505 } 506 else 507 { 508 try 509 { 510 psearchControl = PersistentSearchControl.decodeControl(c); 511 } 512 catch (LDAPException le) 513 { 514 if (debugEnabled()) 515 { 516 TRACER.debugCaught(DebugLogLevel.ERROR, le); 517 } 518 519 throw new DirectoryException( 520 ResultCode.valueOf(le.getResultCode()), 521 le.getMessageObject(), le); 522 } 523 } 524 525 persistentSearch = new PersistentSearch(this, 526 psearchControl.getChangeTypes(), 527 psearchControl.getReturnECs()); 528 setPersistentSearch(persistentSearch); 529 530 // If we're only interested in changes, then we don't actually want 531 // to process the search now. 532 if (psearchControl.getChangesOnly()) 533 { 534 processSearch = false; 535 } 536 } 537 else if (oid.equals(OID_LDAP_SUBENTRIES)) 538 { 539 setReturnLDAPSubentries(true); 540 } 541 else if (oid.equals(OID_MATCHED_VALUES)) 542 { 543 if (c instanceof MatchedValuesControl) 544 { 545 setMatchedValuesControl((MatchedValuesControl) c); 546 } 547 else 548 { 549 try 550 { 551 MatchedValuesControl matchedValuesControl = 552 MatchedValuesControl.decodeControl(c); 553 setMatchedValuesControl(matchedValuesControl); 554 } 555 catch (LDAPException le) 556 { 557 if (debugEnabled()) 558 { 559 TRACER.debugCaught(DebugLogLevel.ERROR, le); 560 } 561 562 throw new DirectoryException( 563 ResultCode.valueOf(le.getResultCode()), 564 le.getMessageObject(), le); 565 } 566 } 567 } 568 else if (oid.equals(OID_ACCOUNT_USABLE_CONTROL)) 569 { 570 setIncludeUsableControl(true); 571 } 572 else if (oid.equals(OID_REAL_ATTRS_ONLY)) 573 { 574 setRealAttributesOnly(true); 575 } 576 else if (oid.equals(OID_VIRTUAL_ATTRS_ONLY)) 577 { 578 setVirtualAttributesOnly(true); 579 } 580 else if (oid.equals(OID_GET_EFFECTIVE_RIGHTS) && 581 DirectoryServer.isSupportedControl(OID_GET_EFFECTIVE_RIGHTS)) 582 { 583 // Do nothing here and let AciHandler deal with it. 584 } 585 586 // NYI -- Add support for additional controls. 587 588 else if (c.isCritical()) 589 { 590 if ((backend == null) || (! backend.supportsControl(oid))) 591 { 592 throw new DirectoryException( 593 ResultCode.UNAVAILABLE_CRITICAL_EXTENSION, 594 ERR_SEARCH_UNSUPPORTED_CRITICAL_CONTROL.get(oid)); 595 } 596 } 597 } 598 } 599 } 600 } 601