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.HashSet; 032 import java.util.List; 033 import java.util.concurrent.locks.Lock; 034 035 import org.opends.server.api.Backend; 036 import org.opends.server.api.ClientConnection; 037 import org.opends.server.api.plugin.PluginResult; 038 import org.opends.server.controls.LDAPAssertionRequestControl; 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.CompareOperation; 043 import org.opends.server.core.CompareOperationWrapper; 044 import org.opends.server.core.DirectoryServer; 045 import org.opends.server.core.PluginConfigManager; 046 import org.opends.server.loggers.debug.DebugTracer; 047 import org.opends.server.types.*; 048 import org.opends.server.types.operation.PostOperationCompareOperation; 049 import org.opends.server.types.operation.PostResponseCompareOperation; 050 import org.opends.server.types.operation.PreOperationCompareOperation; 051 052 import static org.opends.messages.CoreMessages.*; 053 import static org.opends.server.loggers.debug.DebugLogger.*; 054 import static org.opends.server.util.ServerConstants.*; 055 import static org.opends.server.util.StaticUtils.*; 056 057 058 059 /** 060 * This class defines an operation that may be used to determine whether a 061 * specified entry in the Directory Server contains a given attribute-value 062 * pair. 063 */ 064 public class LocalBackendCompareOperation 065 extends CompareOperationWrapper 066 implements PreOperationCompareOperation, PostOperationCompareOperation, 067 PostResponseCompareOperation 068 { 069 /** 070 * The tracer object for the debug logger. 071 */ 072 private static final DebugTracer TRACER = getTracer(); 073 074 075 076 // The backend in which the comparison is to be performed. 077 private Backend backend; 078 079 // The client connection for this operation. 080 private ClientConnection clientConnection; 081 082 // The DN of the entry to compare. 083 private DN entryDN; 084 085 // The entry to be compared. 086 private Entry entry = null; 087 088 089 090 /** 091 * Creates a new compare operation based on the provided compare operation. 092 * 093 * @param compare the compare operation 094 */ 095 public LocalBackendCompareOperation(CompareOperation compare) 096 { 097 super(compare); 098 LocalBackendWorkflowElement.attachLocalOperation (compare, this); 099 } 100 101 102 103 /** 104 * Retrieves the entry to target with the compare operation. 105 * 106 * @return The entry to target with the compare operation, or 107 * <CODE>null</CODE> if the entry is not yet available. 108 */ 109 public Entry getEntryToCompare() 110 { 111 return entry; 112 } 113 114 115 116 /** 117 * Process this compare operation in a local backend. 118 * 119 * @param backend The backend in which the compare operation should be 120 * processed. 121 * 122 * @throws CanceledOperationException if this operation should be 123 * cancelled 124 */ 125 void processLocalCompare(Backend backend) throws CanceledOperationException { 126 boolean executePostOpPlugins = false; 127 128 this.backend = backend; 129 130 clientConnection = getClientConnection(); 131 132 // Get the plugin config manager that will be used for invoking plugins. 133 PluginConfigManager pluginConfigManager = 134 DirectoryServer.getPluginConfigManager(); 135 136 137 // Get a reference to the client connection 138 ClientConnection clientConnection = getClientConnection(); 139 140 141 // Check for a request to cancel this operation. 142 checkIfCanceled(false); 143 144 145 // Create a labeled block of code that we can break out of if a problem is 146 // detected. 147 compareProcessing: 148 { 149 // Process the entry DN to convert it from the raw form to the form 150 // required for the rest of the compare processing. 151 entryDN = getEntryDN(); 152 if (entryDN == null) 153 { 154 break compareProcessing; 155 } 156 157 158 // If the target entry is in the server configuration, then make sure the 159 // requester has the CONFIG_READ privilege. 160 if (DirectoryServer.getConfigHandler().handlesEntry(entryDN) && 161 (! clientConnection.hasPrivilege(Privilege.CONFIG_READ, this))) 162 { 163 appendErrorMessage(ERR_COMPARE_CONFIG_INSUFFICIENT_PRIVILEGES.get()); 164 setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); 165 break compareProcessing; 166 } 167 168 169 // Check for a request to cancel this operation. 170 checkIfCanceled(false); 171 172 173 // Grab a read lock on the entry. 174 Lock readLock = null; 175 for (int i=0; i < 3; i++) 176 { 177 readLock = LockManager.lockRead(entryDN); 178 if (readLock != null) 179 { 180 break; 181 } 182 } 183 184 if (readLock == null) 185 { 186 setResultCode(DirectoryServer.getServerErrorResultCode()); 187 appendErrorMessage(ERR_COMPARE_CANNOT_LOCK_ENTRY.get( 188 String.valueOf(entryDN))); 189 190 break compareProcessing; 191 } 192 193 try 194 { 195 // Get the entry. If it does not exist, then fail. 196 try 197 { 198 entry = DirectoryServer.getEntry(entryDN); 199 200 if (entry == null) 201 { 202 setResultCode(ResultCode.NO_SUCH_OBJECT); 203 appendErrorMessage( 204 ERR_COMPARE_NO_SUCH_ENTRY.get(String.valueOf(entryDN))); 205 206 // See if one of the entry's ancestors exists. 207 DN parentDN = entryDN.getParentDNInSuffix(); 208 while (parentDN != null) 209 { 210 try 211 { 212 if (DirectoryServer.entryExists(parentDN)) 213 { 214 setMatchedDN(parentDN); 215 break; 216 } 217 } 218 catch (Exception e) 219 { 220 if (debugEnabled()) 221 { 222 TRACER.debugCaught(DebugLogLevel.ERROR, e); 223 } 224 break; 225 } 226 227 parentDN = parentDN.getParentDNInSuffix(); 228 } 229 230 break compareProcessing; 231 } 232 } 233 catch (DirectoryException de) 234 { 235 if (debugEnabled()) 236 { 237 TRACER.debugCaught(DebugLogLevel.ERROR, de); 238 } 239 240 setResultCode(de.getResultCode()); 241 appendErrorMessage(de.getMessageObject()); 242 break compareProcessing; 243 } 244 245 // Check to see if there are any controls in the request. If so, then 246 // see if there is any special processing required. 247 try 248 { 249 handleRequestControls(); 250 } 251 catch (DirectoryException de) 252 { 253 if (debugEnabled()) 254 { 255 TRACER.debugCaught(DebugLogLevel.ERROR, de); 256 } 257 258 setResponseData(de); 259 break compareProcessing; 260 } 261 262 263 // Check to see if the client has permission to perform the 264 // compare. 265 266 // FIXME: for now assume that this will check all permission 267 // pertinent to the operation. This includes proxy authorization 268 // and any other controls specified. 269 270 // FIXME: earlier checks to see if the entry already exists may 271 // have already exposed sensitive information to the client. 272 if (! AccessControlConfigManager.getInstance(). 273 getAccessControlHandler().isAllowed(this)) 274 { 275 setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); 276 appendErrorMessage(ERR_COMPARE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get( 277 String.valueOf(entryDN))); 278 break compareProcessing; 279 } 280 281 // Check for a request to cancel this operation. 282 checkIfCanceled(false); 283 284 285 // Invoke the pre-operation compare plugins. 286 executePostOpPlugins = true; 287 PluginResult.PreOperation preOpResult = 288 pluginConfigManager.invokePreOperationComparePlugins(this); 289 if (!preOpResult.continueProcessing()) 290 { 291 setResultCode(preOpResult.getResultCode()); 292 appendErrorMessage(preOpResult.getErrorMessage()); 293 setMatchedDN(preOpResult.getMatchedDN()); 294 setReferralURLs(preOpResult.getReferralURLs()); 295 break compareProcessing; 296 } 297 298 299 // Get the base attribute type and set of options. 300 String baseName; 301 HashSet<String> options; 302 String rawAttributeType = getRawAttributeType(); 303 int semicolonPos = rawAttributeType.indexOf(';'); 304 if (semicolonPos > 0) 305 { 306 baseName = toLowerCase(rawAttributeType.substring(0, semicolonPos)); 307 308 options = new HashSet<String>(); 309 int nextPos = rawAttributeType.indexOf(';', semicolonPos+1); 310 while (nextPos > 0) 311 { 312 options.add(rawAttributeType.substring(semicolonPos+1, nextPos)); 313 semicolonPos = nextPos; 314 nextPos = rawAttributeType.indexOf(';', semicolonPos+1); 315 } 316 317 options.add(rawAttributeType.substring(semicolonPos+1)); 318 } 319 else 320 { 321 baseName = toLowerCase(rawAttributeType); 322 options = null; 323 } 324 325 326 // Actually perform the compare operation. 327 AttributeType attrType = getAttributeType(); 328 if (attrType == null) 329 { 330 attrType = DirectoryServer.getAttributeType(baseName, true); 331 setAttributeType(attrType); 332 } 333 334 List<Attribute> attrList = entry.getAttribute(attrType, options); 335 if ((attrList == null) || attrList.isEmpty()) 336 { 337 setResultCode(ResultCode.NO_SUCH_ATTRIBUTE); 338 if (options == null) 339 { 340 appendErrorMessage(WARN_COMPARE_OP_NO_SUCH_ATTR.get( 341 String.valueOf(entryDN), baseName)); 342 } 343 else 344 { 345 appendErrorMessage(WARN_COMPARE_OP_NO_SUCH_ATTR_WITH_OPTIONS.get( 346 String.valueOf(entryDN), baseName)); 347 } 348 } 349 else 350 { 351 AttributeValue value = new AttributeValue(attrType, 352 getAssertionValue()); 353 354 boolean matchFound = false; 355 for (Attribute a : attrList) 356 { 357 if (a.hasValue(value)) 358 { 359 matchFound = true; 360 break; 361 } 362 } 363 364 if (matchFound) 365 { 366 setResultCode(ResultCode.COMPARE_TRUE); 367 } 368 else 369 { 370 setResultCode(ResultCode.COMPARE_FALSE); 371 } 372 } 373 } 374 finally 375 { 376 LockManager.unlock(entryDN, readLock); 377 } 378 } 379 380 381 // Check for a request to cancel this operation. 382 checkIfCanceled(false); 383 384 385 // Invoke the post-operation compare plugins. 386 if (executePostOpPlugins) 387 { 388 PluginResult.PostOperation postOpResult = 389 pluginConfigManager.invokePostOperationComparePlugins(this); 390 if (!postOpResult.continueProcessing()) 391 { 392 setResultCode(postOpResult.getResultCode()); 393 appendErrorMessage(postOpResult.getErrorMessage()); 394 setMatchedDN(postOpResult.getMatchedDN()); 395 setReferralURLs(postOpResult.getReferralURLs()); 396 } 397 } 398 } 399 400 401 402 /** 403 * Performs any processing required for the controls included in the request. 404 * 405 * @throws DirectoryException If a problem occurs that should prevent the 406 * operation from succeeding. 407 */ 408 private void handleRequestControls() 409 throws DirectoryException 410 { 411 List<Control> requestControls = getRequestControls(); 412 if ((requestControls != null) && (! requestControls.isEmpty())) 413 { 414 for (int i=0; i < requestControls.size(); i++) 415 { 416 Control c = requestControls.get(i); 417 String oid = c.getOID(); 418 419 if (! AccessControlConfigManager.getInstance(). 420 getAccessControlHandler().isAllowed(entryDN, this, c)) 421 { 422 throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 423 ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid)); 424 } 425 426 if (oid.equals(OID_LDAP_ASSERTION)) 427 { 428 LDAPAssertionRequestControl assertControl; 429 if (c instanceof LDAPAssertionRequestControl) 430 { 431 assertControl = (LDAPAssertionRequestControl) c; 432 } 433 else 434 { 435 try 436 { 437 assertControl = LDAPAssertionRequestControl.decodeControl(c); 438 requestControls.set(i, assertControl); 439 } 440 catch (LDAPException le) 441 { 442 if (debugEnabled()) 443 { 444 TRACER.debugCaught(DebugLogLevel.ERROR, le); 445 } 446 447 throw new DirectoryException( 448 ResultCode.valueOf(le.getResultCode()), 449 le.getMessageObject()); 450 } 451 } 452 453 try 454 { 455 // FIXME -- We need to determine whether the current user has 456 // permission to make this determination. 457 SearchFilter filter = assertControl.getSearchFilter(); 458 if (! filter.matchesEntry(entry)) 459 { 460 throw new DirectoryException(ResultCode.ASSERTION_FAILED, 461 ERR_COMPARE_ASSERTION_FAILED.get( 462 String.valueOf(entryDN))); 463 } 464 } 465 catch (DirectoryException de) 466 { 467 if (de.getResultCode() == ResultCode.ASSERTION_FAILED) 468 { 469 throw de; 470 } 471 472 if (debugEnabled()) 473 { 474 TRACER.debugCaught(DebugLogLevel.ERROR, de); 475 } 476 477 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 478 ERR_COMPARE_CANNOT_PROCESS_ASSERTION_FILTER.get( 479 String.valueOf(entryDN), 480 de.getMessageObject())); 481 } 482 } 483 else if (oid.equals(OID_PROXIED_AUTH_V1)) 484 { 485 // The requester must have the PROXIED_AUTH privilige in order to 486 // be able to use this control. 487 if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) 488 { 489 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, 490 ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get()); 491 } 492 493 494 ProxiedAuthV1Control proxyControl; 495 if (c instanceof ProxiedAuthV1Control) 496 { 497 proxyControl = (ProxiedAuthV1Control) c; 498 } 499 else 500 { 501 try 502 { 503 proxyControl = ProxiedAuthV1Control.decodeControl(c); 504 } 505 catch (LDAPException le) 506 { 507 if (debugEnabled()) 508 { 509 TRACER.debugCaught(DebugLogLevel.ERROR, le); 510 } 511 512 throw new DirectoryException( 513 ResultCode.valueOf(le.getResultCode()), 514 le.getMessageObject()); 515 } 516 } 517 518 519 Entry authorizationEntry = proxyControl.getAuthorizationEntry(); 520 setAuthorizationEntry(authorizationEntry); 521 if (authorizationEntry == null) 522 { 523 setProxiedAuthorizationDN(DN.nullDN()); 524 } 525 else 526 { 527 setProxiedAuthorizationDN(authorizationEntry.getDN()); 528 } 529 } 530 else if (oid.equals(OID_PROXIED_AUTH_V2)) 531 { 532 // The requester must have the PROXIED_AUTH privilige in order to 533 // be able to use this control. 534 if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) 535 { 536 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, 537 ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get()); 538 } 539 540 541 ProxiedAuthV2Control proxyControl; 542 if (c instanceof ProxiedAuthV2Control) 543 { 544 proxyControl = (ProxiedAuthV2Control) c; 545 } 546 else 547 { 548 try 549 { 550 proxyControl = ProxiedAuthV2Control.decodeControl(c); 551 } 552 catch (LDAPException le) 553 { 554 if (debugEnabled()) 555 { 556 TRACER.debugCaught(DebugLogLevel.ERROR, le); 557 } 558 559 throw new DirectoryException( 560 ResultCode.valueOf(le.getResultCode()), 561 le.getMessageObject()); 562 } 563 } 564 565 566 Entry authorizationEntry = proxyControl.getAuthorizationEntry(); 567 setAuthorizationEntry(authorizationEntry); 568 if (authorizationEntry == null) 569 { 570 setProxiedAuthorizationDN(DN.nullDN()); 571 } 572 else 573 { 574 setProxiedAuthorizationDN(authorizationEntry.getDN()); 575 } 576 } 577 578 // NYI -- Add support for additional controls. 579 else if (c.isCritical()) 580 { 581 if ((backend == null) || (! backend.supportsControl(oid))) 582 { 583 throw new DirectoryException( 584 ResultCode.UNAVAILABLE_CRITICAL_EXTENSION, 585 ERR_COMPARE_UNSUPPORTED_CRITICAL_CONTROL.get( 586 String.valueOf(entryDN), oid)); 587 } 588 } 589 } 590 } 591 } 592 } 593