opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java
@@ -865,7 +865,11 @@ if (!isBelowFilterThreshold(entryIDSet)) { final EntryIDSet scopeSet = getIDSetFromScope(txn, aBaseDN, searchScope); final int lookThroughLimit = searchOperation.getClientConnection().getLookthroughLimit(); final int idSetLimit = lookThroughLimit == 0 ? SCOPE_IDSET_LIMIT : Math.min(SCOPE_IDSET_LIMIT, lookThroughLimit); final EntryIDSet scopeSet = getIDSetFromScope(txn, aBaseDN, searchScope, idSetLimit); entryIDSet.retainAll(scopeSet); if (debugBuffer != null) { @@ -969,8 +973,8 @@ return null; } private EntryIDSet getIDSetFromScope(final ReadableTransaction txn, DN aBaseDN, SearchScope searchScope) throws DirectoryException private EntryIDSet getIDSetFromScope(final ReadableTransaction txn, DN aBaseDN, SearchScope searchScope, int idSetLimit) throws DirectoryException { final EntryIDSet scopeSet; try @@ -986,14 +990,14 @@ case SINGLE_LEVEL: try (final SequentialCursor<?, EntryID> scopeCursor = dn2id.openChildrenCursor(txn, aBaseDN)) { scopeSet = newIDSetFromCursor(scopeCursor, false); scopeSet = newIDSetFromCursor(scopeCursor, false, idSetLimit); } break; case SUBORDINATES: case WHOLE_SUBTREE: try (final SequentialCursor<?, EntryID> scopeCursor = dn2id.openSubordinatesCursor(txn, aBaseDN)) { scopeSet = newIDSetFromCursor(scopeCursor, searchScope.equals(SearchScope.WHOLE_SUBTREE)); scopeSet = newIDSetFromCursor(scopeCursor, searchScope.equals(SearchScope.WHOLE_SUBTREE), idSetLimit); } break; default: @@ -1016,23 +1020,32 @@ } } private static EntryIDSet newIDSetFromCursor(SequentialCursor<?, EntryID> cursor, boolean includeCurrent) private static EntryIDSet newIDSetFromCursor(SequentialCursor<?, EntryID> cursor, boolean includeCurrent, int idSetLimit) { long ids[] = new long[SCOPE_IDSET_LIMIT]; long entryIDs[] = new long[idSetLimit]; int offset = 0; if (includeCurrent) { ids[offset++] = cursor.getValue().longValue(); } for(; offset < ids.length && cursor.next() ; offset++) { ids[offset] = cursor.getValue().longValue(); if (includeCurrent) { entryIDs[offset++] = cursor.getValue().longValue(); } ids = Arrays.copyOf(ids, offset); Arrays.sort(ids); while(offset < idSetLimit && cursor.next()) { entryIDs[offset++] = cursor.getValue().longValue(); } return offset == SCOPE_IDSET_LIMIT ? EntryIDSet.newUndefinedSet() : EntryIDSet.newDefinedSet(ids); if (offset == idSetLimit && cursor.next()) { return EntryIDSet.newUndefinedSet(); } else if (offset != idSetLimit) { entryIDs = Arrays.copyOf(entryIDs, offset); } Arrays.sort(entryIDs); return EntryIDSet.newDefinedSet(entryIDs); } private <E1 extends Exception, E2 extends Exception> opendj-sdk/opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/ClientConnectionStub.java
New file @@ -0,0 +1,198 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2015 ForgeRock AS */ package org.opends.server.backends.pluggable; import java.net.InetAddress; import java.util.Collection; import org.forgerock.i18n.LocalizableMessage; import org.opends.server.api.ClientConnection; import org.opends.server.api.ConnectionHandler; import org.opends.server.core.SearchOperation; import org.opends.server.types.CancelRequest; import org.opends.server.types.CancelResult; import org.opends.server.types.DirectoryException; import org.opends.server.types.DisconnectReason; import org.opends.server.types.IntermediateResponse; import org.opends.server.types.Operation; import org.opends.server.types.SearchResultEntry; import org.opends.server.types.SearchResultReference; class ClientConnectionStub extends ClientConnection { @Override public long getConnectionID() { return 0; } @Override public ConnectionHandler<?> getConnectionHandler() { return null; } @Override public String getProtocol() { return null; } @Override public String getClientAddress() { return null; } @Override public int getClientPort() { return 0; } @Override public String getServerAddress() { return null; } @Override public int getServerPort() { return 0; } @Override public InetAddress getRemoteAddress() { return null; } @Override public InetAddress getLocalAddress() { return null; } @Override public boolean isConnectionValid() { return false; } @Override public boolean isSecure() { return false; } @Override public long getNumberOfOperations() { return 0; } @Override public void sendResponse(Operation operation) { } @Override public void sendSearchEntry(SearchOperation searchOperation, SearchResultEntry searchEntry) throws DirectoryException { } @Override public boolean sendSearchReference(SearchOperation searchOperation, SearchResultReference searchReference) throws DirectoryException { return false; } @Override protected boolean sendIntermediateResponseMessage(IntermediateResponse intermediateResponse) { return false; } @Override public void disconnect(DisconnectReason disconnectReason, boolean sendNotification, LocalizableMessage message) { } @Override public Collection<Operation> getOperationsInProgress() { return null; } @Override public Operation getOperationInProgress(int messageID) { return null; } @Override public boolean removeOperationInProgress(int messageID) { return false; } @Override public CancelResult cancelOperation(int messageID, CancelRequest cancelRequest) { return null; } @Override public void cancelAllOperations(CancelRequest cancelRequest) { } @Override public void cancelAllOperationsExcept(CancelRequest cancelRequest, int messageID) { } @Override public String getMonitorSummary() { return null; } @Override public void toString(StringBuilder buffer) { } @Override public int getSSF() { return 0; } } opendj-sdk/opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java
@@ -25,6 +25,7 @@ */ package org.opends.server.backends.pluggable; import static org.assertj.core.api.Assertions.assertThat; import static org.forgerock.opendj.ldap.ModificationType.ADD; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -37,14 +38,18 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Random; import org.assertj.core.api.Assertions; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.ConditionResult; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.SearchScope; import org.opends.server.DirectoryServerTestCase; import org.opends.server.TestCaseUtils; @@ -54,7 +59,10 @@ import org.opends.server.admin.std.server.BackendVLVIndexCfg; import org.opends.server.admin.std.server.PluggableBackendCfg; import org.opends.server.api.Backend.BackendOperation; import org.opends.server.api.ClientConnection; import org.opends.server.api.ConnectionHandler; import org.opends.server.core.DirectoryServer; import org.opends.server.core.SearchOperation; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.protocols.internal.InternalSearchOperation; import org.opends.server.protocols.internal.SearchRequest; @@ -62,14 +70,20 @@ import org.opends.server.types.AttributeType; import org.opends.server.types.BackupConfig; import org.opends.server.types.BackupDirectory; import org.opends.server.types.CancelRequest; import org.opends.server.types.CancelResult; import org.opends.server.types.DN; import org.opends.server.types.DirectoryException; import org.opends.server.types.DisconnectReason; import org.opends.server.types.Entry; import org.opends.server.types.IntermediateResponse; import org.opends.server.types.LDIFExportConfig; import org.opends.server.types.LDIFImportConfig; import org.opends.server.types.Modification; import org.opends.server.types.Operation; import org.opends.server.types.RestoreConfig; import org.opends.server.types.SearchResultEntry; import org.opends.server.types.SearchResultReference; import org.opends.server.workflowelement.localbackend.LocalBackendSearchOperation; import org.testng.Reporter; import org.testng.annotations.AfterClass; @@ -716,6 +730,30 @@ subTreeSearch(true); } @Test(dependsOnMethods = "testAdd") public void testSearchIsConsideredUnindexedBasedOnLookThroughLimit() throws DirectoryException { final int nbEntries = topEntries.size() + entries.size() + workEntries.size(); final SearchRequest request = newSearchRequest(testBaseDN, SearchScope.WHOLE_SUBTREE, "objectclass=*"); final ClientConnection connection = new ClientConnectionStub(); connection.setLookthroughLimit(0); InternalSearchOperation searchOperation = new InternalSearchOperation(connection, 1, 1, request, null); searchOperation.run(); assertThat(searchOperation.getEntriesSent()).isEqualTo(nbEntries); connection.setLookthroughLimit(nbEntries); searchOperation = new InternalSearchOperation(connection, 1, 1, request, null); searchOperation.run(); assertThat(searchOperation.getEntriesSent()).isEqualTo(nbEntries); connection.setLookthroughLimit(nbEntries - 1); searchOperation = new InternalSearchOperation(connection, 1, 1, request, null); searchOperation.run(); assertThat(searchOperation.getResultCode()).isEqualTo(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); assertThat(searchOperation.getErrorMessage().toString()).contains("not have sufficient privileges", "unindexed search"); assertThat(searchOperation.getEntriesSent()).isEqualTo(0); } private void subTreeSearch(boolean useInternalConnection) throws Exception { SearchRequest request = newSearchRequest(testBaseDN, SearchScope.WHOLE_SUBTREE, "objectclass=*");