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.Iterator; 032 import java.util.List; 033 import java.util.concurrent.locks.Lock; 034 035 import org.opends.messages.Message; 036 import org.opends.server.api.Backend; 037 import org.opends.server.api.ChangeNotificationListener; 038 import org.opends.server.api.ClientConnection; 039 import org.opends.server.api.SynchronizationProvider; 040 import org.opends.server.api.plugin.PluginResult; 041 import org.opends.server.controls.LDAPAssertionRequestControl; 042 import org.opends.server.controls.LDAPPreReadRequestControl; 043 import org.opends.server.controls.LDAPPreReadResponseControl; 044 import org.opends.server.controls.ProxiedAuthV1Control; 045 import org.opends.server.controls.ProxiedAuthV2Control; 046 import org.opends.server.core.AccessControlConfigManager; 047 import org.opends.server.core.DeleteOperationWrapper; 048 import org.opends.server.core.DeleteOperation; 049 import org.opends.server.core.DirectoryServer; 050 import org.opends.server.core.PluginConfigManager; 051 import org.opends.server.loggers.debug.DebugTracer; 052 import org.opends.server.types.AttributeType; 053 import org.opends.server.types.CanceledOperationException; 054 import org.opends.server.types.Control; 055 import org.opends.server.types.DebugLogLevel; 056 import org.opends.server.types.DirectoryException; 057 import org.opends.server.types.DN; 058 import org.opends.server.types.Entry; 059 import org.opends.server.types.LDAPException; 060 import org.opends.server.types.LockManager; 061 import org.opends.server.types.Privilege; 062 import org.opends.server.types.ResultCode; 063 import org.opends.server.types.SearchFilter; 064 import org.opends.server.types.SearchResultEntry; 065 import org.opends.server.types.SynchronizationProviderResult; 066 import org.opends.server.types.operation.PostOperationDeleteOperation; 067 import org.opends.server.types.operation.PostResponseDeleteOperation; 068 import org.opends.server.types.operation.PreOperationDeleteOperation; 069 import org.opends.server.types.operation.PostSynchronizationDeleteOperation; 070 071 import static org.opends.messages.CoreMessages.*; 072 import static org.opends.server.loggers.ErrorLogger.*; 073 import static org.opends.server.loggers.debug.DebugLogger.*; 074 import static org.opends.server.util.ServerConstants.*; 075 import static org.opends.server.util.StaticUtils.*; 076 077 078 079 /** 080 * This class defines an operation used to delete an entry in a local backend 081 * of the Directory Server. 082 */ 083 public class LocalBackendDeleteOperation 084 extends DeleteOperationWrapper 085 implements PreOperationDeleteOperation, PostOperationDeleteOperation, 086 PostResponseDeleteOperation, 087 PostSynchronizationDeleteOperation 088 { 089 /** 090 * The tracer object for the debug logger. 091 */ 092 private static final DebugTracer TRACER = getTracer(); 093 094 095 096 // The backend in which the operation is to be processed. 097 private Backend backend; 098 099 // Indicates whether the LDAP no-op control has been requested. 100 private boolean noOp; 101 102 // The client connection on which this operation was requested. 103 private ClientConnection clientConnection; 104 105 // The DN of the entry to be deleted. 106 private DN entryDN; 107 108 // The entry to be deleted. 109 private Entry entry; 110 111 // The pre-read request control included in the request, if applicable. 112 private LDAPPreReadRequestControl preReadRequest; 113 114 115 116 /** 117 * Creates a new operation that may be used to delete an entry from a 118 * local backend of the Directory Server. 119 * 120 * @param delete The operation to enhance. 121 */ 122 public LocalBackendDeleteOperation(DeleteOperation delete) 123 { 124 super(delete); 125 LocalBackendWorkflowElement.attachLocalOperation (delete, this); 126 } 127 128 129 130 /** 131 * Retrieves the entry to be deleted. 132 * 133 * @return The entry to be deleted, or <CODE>null</CODE> if the entry is not 134 * yet available. 135 */ 136 public Entry getEntryToDelete() 137 { 138 return entry; 139 } 140 141 142 143 /** 144 * Process this delete operation in a local backend. 145 * 146 * @param backend The backend in which the delete operation should be 147 * processed. 148 * 149 * @throws CanceledOperationException if this operation should be 150 * cancelled 151 */ 152 void processLocalDelete(Backend backend) throws CanceledOperationException { 153 boolean executePostOpPlugins = false; 154 155 this.backend = backend; 156 157 clientConnection = getClientConnection(); 158 159 // Get the plugin config manager that will be used for invoking plugins. 160 PluginConfigManager pluginConfigManager = 161 DirectoryServer.getPluginConfigManager(); 162 163 // Check for a request to cancel this operation. 164 checkIfCanceled(false); 165 166 // Create a labeled block of code that we can break out of if a problem is 167 // detected. 168 deleteProcessing: 169 { 170 // Process the entry DN to convert it from its raw form as provided by the 171 // client to the form required for the rest of the delete processing. 172 entryDN = getEntryDN(); 173 if (entryDN == null){ 174 break deleteProcessing; 175 } 176 177 // Grab a write lock on the entry. 178 Lock entryLock = null; 179 for (int i=0; i < 3; i++) 180 { 181 entryLock = LockManager.lockWrite(entryDN); 182 if (entryLock != null) 183 { 184 break; 185 } 186 } 187 188 if (entryLock == null) 189 { 190 setResultCode(DirectoryServer.getServerErrorResultCode()); 191 appendErrorMessage(ERR_DELETE_CANNOT_LOCK_ENTRY.get( 192 String.valueOf(entryDN))); 193 break deleteProcessing; 194 } 195 196 try 197 { 198 // Get the entry to delete. If it doesn't exist, then fail. 199 try 200 { 201 entry = backend.getEntry(entryDN); 202 if (entry == null) 203 { 204 setResultCode(ResultCode.NO_SUCH_OBJECT); 205 appendErrorMessage(ERR_DELETE_NO_SUCH_ENTRY.get( 206 String.valueOf(entryDN))); 207 208 try 209 { 210 DN parentDN = entryDN.getParentDNInSuffix(); 211 while (parentDN != null) 212 { 213 if (DirectoryServer.entryExists(parentDN)) 214 { 215 setMatchedDN(parentDN); 216 break; 217 } 218 219 parentDN = parentDN.getParentDNInSuffix(); 220 } 221 } 222 catch (Exception e) 223 { 224 if (debugEnabled()) 225 { 226 TRACER.debugCaught(DebugLogLevel.ERROR, e); 227 } 228 } 229 230 break deleteProcessing; 231 } 232 } 233 catch (DirectoryException de) 234 { 235 if (debugEnabled()) 236 { 237 TRACER.debugCaught(DebugLogLevel.ERROR, de); 238 } 239 240 setResponseData(de); 241 break deleteProcessing; 242 } 243 244 245 // Invoke any conflict resolution processing that might be needed by the 246 // synchronization provider. 247 for (SynchronizationProvider provider : 248 DirectoryServer.getSynchronizationProviders()) 249 { 250 try 251 { 252 SynchronizationProviderResult result = 253 provider.handleConflictResolution(this); 254 if (! result.continueProcessing()) 255 { 256 setResultCode(result.getResultCode()); 257 appendErrorMessage(result.getErrorMessage()); 258 setMatchedDN(result.getMatchedDN()); 259 setReferralURLs(result.getReferralURLs()); 260 break deleteProcessing; 261 } 262 } 263 catch (DirectoryException de) 264 { 265 if (debugEnabled()) 266 { 267 TRACER.debugCaught(DebugLogLevel.ERROR, de); 268 } 269 270 logError(ERR_DELETE_SYNCH_CONFLICT_RESOLUTION_FAILED.get( 271 getConnectionID(), getOperationID(), 272 getExceptionMessage(de))); 273 setResponseData(de); 274 break deleteProcessing; 275 } 276 } 277 278 // Check to see if the client has permission to perform the 279 // delete. 280 281 // Check to see if there are any controls in the request. If so, then 282 // see if there is any special processing required. 283 try 284 { 285 handleRequestControls(); 286 } 287 catch (DirectoryException de) 288 { 289 if (debugEnabled()) 290 { 291 TRACER.debugCaught(DebugLogLevel.ERROR, de); 292 } 293 294 setResponseData(de); 295 break deleteProcessing; 296 } 297 298 299 // FIXME: for now assume that this will check all permission 300 // pertinent to the operation. This includes proxy authorization 301 // and any other controls specified. 302 303 // FIXME: earlier checks to see if the entry already exists may 304 // have already exposed sensitive information to the client. 305 if (! AccessControlConfigManager.getInstance(). 306 getAccessControlHandler().isAllowed(this)) 307 { 308 setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); 309 appendErrorMessage(ERR_DELETE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get( 310 String.valueOf(entryDN))); 311 break deleteProcessing; 312 } 313 314 // Check for a request to cancel this operation. 315 checkIfCanceled(false); 316 317 318 // If the operation is not a synchronization operation, 319 // invoke the pre-delete plugins. 320 if (! isSynchronizationOperation()) 321 { 322 executePostOpPlugins = true; 323 PluginResult.PreOperation preOpResult = 324 pluginConfigManager.invokePreOperationDeletePlugins(this); 325 if (!preOpResult.continueProcessing()) 326 { 327 setResultCode(preOpResult.getResultCode()); 328 appendErrorMessage(preOpResult.getErrorMessage()); 329 setMatchedDN(preOpResult.getMatchedDN()); 330 setReferralURLs(preOpResult.getReferralURLs()); 331 break deleteProcessing; 332 } 333 } 334 335 336 // Check for a request to cancel this operation. 337 checkIfCanceled(true); 338 339 340 // Get the backend to use for the delete. If there is none, then fail. 341 if (backend == null) 342 { 343 setResultCode(ResultCode.NO_SUCH_OBJECT); 344 appendErrorMessage(ERR_DELETE_NO_SUCH_ENTRY.get( 345 String.valueOf(entryDN))); 346 break deleteProcessing; 347 } 348 349 350 // If it is not a private backend, then check to see if the server or 351 // backend is operating in read-only mode. 352 if (! backend.isPrivateBackend()) 353 { 354 switch (DirectoryServer.getWritabilityMode()) 355 { 356 case DISABLED: 357 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 358 appendErrorMessage(ERR_DELETE_SERVER_READONLY.get( 359 String.valueOf(entryDN))); 360 break deleteProcessing; 361 362 case INTERNAL_ONLY: 363 if (! (isInternalOperation() || isSynchronizationOperation())) 364 { 365 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 366 appendErrorMessage(ERR_DELETE_SERVER_READONLY.get( 367 String.valueOf(entryDN))); 368 break deleteProcessing; 369 } 370 } 371 372 switch (backend.getWritabilityMode()) 373 { 374 case DISABLED: 375 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 376 appendErrorMessage(ERR_DELETE_BACKEND_READONLY.get( 377 String.valueOf(entryDN))); 378 break deleteProcessing; 379 380 case INTERNAL_ONLY: 381 if (! (isInternalOperation() || isSynchronizationOperation())) 382 { 383 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 384 appendErrorMessage(ERR_DELETE_BACKEND_READONLY.get( 385 String.valueOf(entryDN))); 386 break deleteProcessing; 387 } 388 } 389 } 390 391 392 // The selected backend will have the responsibility of making sure that 393 // the entry actually exists and does not have any children (or possibly 394 // handling a subtree delete). But we will need to check if there are 395 // any subordinate backends that should stop us from attempting the 396 // delete. 397 Backend[] subBackends = backend.getSubordinateBackends(); 398 for (Backend b : subBackends) 399 { 400 DN[] baseDNs = b.getBaseDNs(); 401 for (DN dn : baseDNs) 402 { 403 if (dn.isDescendantOf(entryDN)) 404 { 405 setResultCode(ResultCode.NOT_ALLOWED_ON_NONLEAF); 406 appendErrorMessage(ERR_DELETE_HAS_SUB_BACKEND.get( 407 String.valueOf(entryDN), 408 String.valueOf(dn))); 409 break deleteProcessing; 410 } 411 } 412 } 413 414 415 // Actually perform the delete. 416 try 417 { 418 if (noOp) 419 { 420 setResultCode(ResultCode.NO_OPERATION); 421 appendErrorMessage(INFO_DELETE_NOOP.get()); 422 } 423 else 424 { 425 for (SynchronizationProvider provider : 426 DirectoryServer.getSynchronizationProviders()) 427 { 428 try 429 { 430 SynchronizationProviderResult result = 431 provider.doPreOperation(this); 432 if (! result.continueProcessing()) 433 { 434 setResultCode(result.getResultCode()); 435 appendErrorMessage(result.getErrorMessage()); 436 setMatchedDN(result.getMatchedDN()); 437 setReferralURLs(result.getReferralURLs()); 438 break deleteProcessing; 439 } 440 } 441 catch (DirectoryException de) 442 { 443 if (debugEnabled()) 444 { 445 TRACER.debugCaught(DebugLogLevel.ERROR, de); 446 } 447 448 logError(ERR_DELETE_SYNCH_PREOP_FAILED.get(getConnectionID(), 449 getOperationID(), getExceptionMessage(de))); 450 setResponseData(de); 451 break deleteProcessing; 452 } 453 } 454 455 backend.deleteEntry(entryDN, this); 456 } 457 458 459 processPreReadControl(); 460 461 462 if (! noOp) 463 { 464 setResultCode(ResultCode.SUCCESS); 465 } 466 } 467 catch (DirectoryException de) 468 { 469 if (debugEnabled()) 470 { 471 TRACER.debugCaught(DebugLogLevel.ERROR, de); 472 } 473 474 setResponseData(de); 475 break deleteProcessing; 476 } 477 } 478 finally 479 { 480 LockManager.unlock(entryDN, entryLock); 481 } 482 } 483 484 485 for (SynchronizationProvider provider : 486 DirectoryServer.getSynchronizationProviders()) 487 { 488 try 489 { 490 provider.doPostOperation(this); 491 } 492 catch (DirectoryException de) 493 { 494 if (debugEnabled()) 495 { 496 TRACER.debugCaught(DebugLogLevel.ERROR, de); 497 } 498 499 logError(ERR_DELETE_SYNCH_POSTOP_FAILED.get(getConnectionID(), 500 getOperationID(), getExceptionMessage(de))); 501 setResponseData(de); 502 break; 503 } 504 } 505 506 // Invoke the post-operation or post-synchronization delete plugins. 507 if (isSynchronizationOperation()) 508 { 509 if (getResultCode() == ResultCode.SUCCESS) 510 { 511 pluginConfigManager.invokePostSynchronizationDeletePlugins(this); 512 } 513 } 514 else if (executePostOpPlugins) 515 { 516 PluginResult.PostOperation postOpResult = 517 pluginConfigManager.invokePostOperationDeletePlugins(this); 518 if (!postOpResult.continueProcessing()) 519 { 520 setResultCode(postOpResult.getResultCode()); 521 appendErrorMessage(postOpResult.getErrorMessage()); 522 setMatchedDN(postOpResult.getMatchedDN()); 523 setReferralURLs(postOpResult.getReferralURLs()); 524 return; 525 } 526 } 527 528 529 // Notify any change notification listeners that might be registered with 530 // the server. 531 if (getResultCode() == ResultCode.SUCCESS) 532 { 533 for (ChangeNotificationListener changeListener : 534 DirectoryServer.getChangeNotificationListeners()) 535 { 536 try 537 { 538 changeListener.handleDeleteOperation(this, entry); 539 } 540 catch (Exception e) 541 { 542 if (debugEnabled()) 543 { 544 TRACER.debugCaught(DebugLogLevel.ERROR, e); 545 } 546 547 Message message = ERR_DELETE_ERROR_NOTIFYING_CHANGE_LISTENER.get( 548 getExceptionMessage(e)); 549 logError(message); 550 } 551 } 552 } 553 } 554 555 556 557 /** 558 * Performs any request control processing needed for this operation. 559 * 560 * @throws DirectoryException If a problem occurs that should cause the 561 * operation to fail. 562 */ 563 private void handleRequestControls() 564 throws DirectoryException 565 { 566 List<Control> requestControls = getRequestControls(); 567 if ((requestControls != null) && (! requestControls.isEmpty())) 568 { 569 for (int i=0; i < requestControls.size(); i++) 570 { 571 Control c = requestControls.get(i); 572 String oid = c.getOID(); 573 574 if (!AccessControlConfigManager.getInstance(). 575 getAccessControlHandler().isAllowed(entryDN, this, c)) 576 { 577 throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 578 ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid)); 579 } 580 581 if (oid.equals(OID_LDAP_ASSERTION)) 582 { 583 LDAPAssertionRequestControl assertControl; 584 if (c instanceof LDAPAssertionRequestControl) 585 { 586 assertControl = (LDAPAssertionRequestControl) c; 587 } 588 else 589 { 590 try 591 { 592 assertControl = LDAPAssertionRequestControl.decodeControl(c); 593 requestControls.set(i, assertControl); 594 } 595 catch (LDAPException le) 596 { 597 if (debugEnabled()) 598 { 599 TRACER.debugCaught(DebugLogLevel.ERROR, le); 600 } 601 602 throw new DirectoryException( 603 ResultCode.valueOf(le.getResultCode()), 604 le.getMessageObject()); 605 } 606 } 607 608 try 609 { 610 // FIXME -- We need to determine whether the current user has 611 // permission to make this determination. 612 SearchFilter filter = assertControl.getSearchFilter(); 613 if (! filter.matchesEntry(entry)) 614 { 615 throw new DirectoryException(ResultCode.ASSERTION_FAILED, 616 ERR_DELETE_ASSERTION_FAILED.get( 617 String.valueOf(entryDN))); 618 } 619 } 620 catch (DirectoryException de) 621 { 622 if (de.getResultCode() == ResultCode.ASSERTION_FAILED) 623 { 624 throw de; 625 } 626 627 if (debugEnabled()) 628 { 629 TRACER.debugCaught(DebugLogLevel.ERROR, de); 630 } 631 632 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 633 ERR_DELETE_CANNOT_PROCESS_ASSERTION_FILTER.get( 634 String.valueOf(entryDN), 635 de.getMessageObject())); 636 } 637 } 638 else if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED)) 639 { 640 noOp = true; 641 } 642 else if (oid.equals(OID_LDAP_READENTRY_PREREAD)) 643 { 644 if (c instanceof LDAPPreReadRequestControl) 645 { 646 preReadRequest = (LDAPPreReadRequestControl) c; 647 } 648 else 649 { 650 try 651 { 652 preReadRequest = LDAPPreReadRequestControl.decodeControl(c); 653 requestControls.set(i, preReadRequest); 654 } 655 catch (LDAPException le) 656 { 657 if (debugEnabled()) 658 { 659 TRACER.debugCaught(DebugLogLevel.ERROR, le); 660 } 661 662 throw new DirectoryException( 663 ResultCode.valueOf(le.getResultCode()), 664 le.getMessageObject()); 665 } 666 } 667 } 668 else if (oid.equals(OID_PROXIED_AUTH_V1)) 669 { 670 // The requester must have the PROXIED_AUTH privilige in order to 671 // be able to use this control. 672 if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) 673 { 674 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, 675 ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get()); 676 } 677 678 679 ProxiedAuthV1Control proxyControl; 680 if (c instanceof ProxiedAuthV1Control) 681 { 682 proxyControl = (ProxiedAuthV1Control) c; 683 } 684 else 685 { 686 try 687 { 688 proxyControl = ProxiedAuthV1Control.decodeControl(c); 689 } 690 catch (LDAPException le) 691 { 692 if (debugEnabled()) 693 { 694 TRACER.debugCaught(DebugLogLevel.ERROR, le); 695 } 696 697 throw new DirectoryException( 698 ResultCode.valueOf(le.getResultCode()), 699 le.getMessageObject()); 700 } 701 } 702 703 704 Entry authorizationEntry = proxyControl.getAuthorizationEntry(); 705 setAuthorizationEntry(authorizationEntry); 706 if (authorizationEntry == null) 707 { 708 setProxiedAuthorizationDN(DN.nullDN()); 709 } 710 else 711 { 712 setProxiedAuthorizationDN(authorizationEntry.getDN()); 713 } 714 } 715 else if (oid.equals(OID_PROXIED_AUTH_V2)) 716 { 717 // The requester must have the PROXIED_AUTH privilige in order to 718 // be able to use this control. 719 if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) 720 { 721 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, 722 ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get()); 723 } 724 725 726 ProxiedAuthV2Control proxyControl; 727 if (c instanceof ProxiedAuthV2Control) 728 { 729 proxyControl = (ProxiedAuthV2Control) c; 730 } 731 else 732 { 733 try 734 { 735 proxyControl = ProxiedAuthV2Control.decodeControl(c); 736 } 737 catch (LDAPException le) 738 { 739 if (debugEnabled()) 740 { 741 TRACER.debugCaught(DebugLogLevel.ERROR, le); 742 } 743 744 throw new DirectoryException( 745 ResultCode.valueOf(le.getResultCode()), 746 le.getMessageObject()); 747 } 748 } 749 750 751 Entry authorizationEntry = proxyControl.getAuthorizationEntry(); 752 setAuthorizationEntry(authorizationEntry); 753 if (authorizationEntry == null) 754 { 755 setProxiedAuthorizationDN(DN.nullDN()); 756 } 757 else 758 { 759 setProxiedAuthorizationDN(authorizationEntry.getDN()); 760 } 761 } 762 763 // NYI -- Add support for additional controls. 764 765 else if (c.isCritical()) 766 { 767 if ((backend == null) || (! backend.supportsControl(oid))) 768 { 769 throw new DirectoryException( 770 ResultCode.UNAVAILABLE_CRITICAL_EXTENSION, 771 ERR_DELETE_UNSUPPORTED_CRITICAL_CONTROL.get( 772 String.valueOf(entryDN), oid)); 773 } 774 } 775 } 776 } 777 } 778 779 780 781 /** 782 * Performs any processing needed for the LDAP pre-read control. 783 */ 784 private void processPreReadControl() 785 { 786 if (preReadRequest != null) 787 { 788 Entry entryCopy = entry.duplicate(true); 789 790 if (! preReadRequest.allowsAttribute( 791 DirectoryServer.getObjectClassAttributeType())) 792 { 793 entryCopy.removeAttribute( 794 DirectoryServer.getObjectClassAttributeType()); 795 } 796 797 if (! preReadRequest.returnAllUserAttributes()) 798 { 799 Iterator<AttributeType> iterator = 800 entryCopy.getUserAttributes().keySet().iterator(); 801 while (iterator.hasNext()) 802 { 803 AttributeType attrType = iterator.next(); 804 if (! preReadRequest.allowsAttribute(attrType)) 805 { 806 iterator.remove(); 807 } 808 } 809 } 810 811 if (! preReadRequest.returnAllOperationalAttributes()) 812 { 813 Iterator<AttributeType> iterator = 814 entryCopy.getOperationalAttributes().keySet().iterator(); 815 while (iterator.hasNext()) 816 { 817 AttributeType attrType = iterator.next(); 818 if (! preReadRequest.allowsAttribute(attrType)) 819 { 820 iterator.remove(); 821 } 822 } 823 } 824 825 // FIXME -- Check access controls on the entry to see if it should 826 // be returned or if any attributes need to be stripped 827 // out.. 828 SearchResultEntry searchEntry = new SearchResultEntry(entryCopy); 829 LDAPPreReadResponseControl responseControl = 830 new LDAPPreReadResponseControl(preReadRequest.getOID(), 831 preReadRequest.isCritical(), 832 searchEntry); 833 addResponseControl(responseControl); 834 } 835 } 836 } 837