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