Added privileges support for unindexed searches. Fixed issue where id2subtree and id2children indexes were not used when they should be.
Added test cases for the unindexed search privilege.
Fix for issue 480.
| | |
| | | ds-cfg-default-root-privilege-name: update-schema |
| | | ds-cfg-default-root-privilege-name: privilege-change |
| | | ds-cfg-default-root-privilege-name: index-rebuild |
| | | ds-cfg-default-root-privilege-name: unindexed-search |
| | | |
| | | dn: cn=Directory Manager,cn=Root DNs,cn=config |
| | | objectClass: top |
| | |
| | | import org.opends.server.api.AttributeSyntax; |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.api.EntryCache; |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.core.AddOperation; |
| | | import org.opends.server.core.DeleteOperation; |
| | | import org.opends.server.core.DirectoryServer; |
| | |
| | | import org.opends.server.controls.ServerSideSortRequestControl; |
| | | import org.opends.server.controls.ServerSideSortResponseControl; |
| | | import org.opends.server.controls.VLVRequestControl; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.CancelledOperationException; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.LockType; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.RDN; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.SearchScope; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.util.StaticUtils; |
| | | import org.opends.server.util.ServerConstants; |
| | | |
| | |
| | | |
| | | // Evaluate the search scope against the id2children and id2subtree indexes. |
| | | boolean candidatesAreInScope = false; |
| | | if (entryIDList.isDefined() && |
| | | entryIDList.size() > IndexFilter.FILTER_CANDIDATE_THRESHOLD) |
| | | if (entryIDList.size() > IndexFilter.FILTER_CANDIDATE_THRESHOLD) |
| | | { |
| | | // Read the ID from dn2id. |
| | | EntryID baseID = dn2id.get(null, baseDN); |
| | |
| | | } |
| | | else |
| | | { |
| | | ClientConnection clientConnection = |
| | | searchOperation.getClientConnection(); |
| | | if(! clientConnection.hasPrivilege(Privilege.UNINDEXED_SEARCH, |
| | | searchOperation)) |
| | | { |
| | | int msgID = MSGID_JEB_SEARCH_UNINDEXED_INSUFFICIENT_PRIVILEGES; |
| | | String message = getMessage(msgID); |
| | | throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, |
| | | message, msgID); |
| | | } |
| | | |
| | | if (sortRequest != null) |
| | | { |
| | | // FIXME -- Add support for sorting unindexed searches using indexes |
| | |
| | | CATEGORY_MASK_JEB | SEVERITY_MASK_MILD_ERROR | 144; |
| | | |
| | | /** |
| | | * The message ID of an error indicating that unindexed searches are not |
| | | * allowed without the unindexed search prilvilege. This does not take |
| | | * any arguments. |
| | | */ |
| | | public static final int MSGID_JEB_SEARCH_UNINDEXED_INSUFFICIENT_PRIVILEGES = |
| | | CATEGORY_MASK_JEB | SEVERITY_MASK_MILD_ERROR | 145; |
| | | |
| | | /** |
| | | * Associates a set of generic messages with the message IDs defined in this |
| | | * class. |
| | | */ |
| | |
| | | "Unable to prcess the virtual list view request because " + |
| | | "no entry was found in the result set with a sort value " + |
| | | "greater than or equal to the provided assertion value"); |
| | | registerMessage(MSGID_JEB_SEARCH_UNINDEXED_INSUFFICIENT_PRIVILEGES, |
| | | "You do not have sufficient privileges to perform an " + |
| | | "unindexed search"); |
| | | } |
| | | } |
| | |
| | | * The privilege that provides the ability to rebuild one or more |
| | | * indexes in a backend that supports indexing. |
| | | */ |
| | | INDEX_REBUILD("index-rebuild"); |
| | | INDEX_REBUILD("index-rebuild"), |
| | | |
| | | |
| | | |
| | | /** |
| | | * The privilege that provides the ability to perform an unindexed |
| | | * search in the JE backend. |
| | | */ |
| | | UNINDEXED_SEARCH("unindexed-search"); |
| | | |
| | | |
| | | |
| | |
| | | PRIV_MAP.put("update-schema", UPDATE_SCHEMA); |
| | | PRIV_MAP.put("privilege-change", PRIVILEGE_CHANGE); |
| | | PRIV_MAP.put("index-rebuild", INDEX_REBUILD); |
| | | PRIV_MAP.put("unindexed-search", UNINDEXED_SEARCH); |
| | | |
| | | PRIV_NAMES.add("bypass-acl"); |
| | | PRIV_NAMES.add("modify-acl"); |
| | |
| | | PRIV_NAMES.add("update-schema"); |
| | | PRIV_NAMES.add("privilege-change"); |
| | | PRIV_NAMES.add("index-rebuild"); |
| | | PRIV_NAMES.add("unindexed-search"); |
| | | |
| | | DEFAULT_ROOT_PRIV_SET.add(BYPASS_ACL); |
| | | DEFAULT_ROOT_PRIV_SET.add(MODIFY_ACL); |
| | |
| | | DEFAULT_ROOT_PRIV_SET.add(UPDATE_SCHEMA); |
| | | DEFAULT_ROOT_PRIV_SET.add(PRIVILEGE_CHANGE); |
| | | DEFAULT_ROOT_PRIV_SET.add(INDEX_REBUILD); |
| | | DEFAULT_ROOT_PRIV_SET.add(UNINDEXED_SEARCH); |
| | | } |
| | | |
| | | |
| | |
| | | replace: ds-cfg-override-severity |
| | | ds-cfg-override-severity: EXCEPTION=INFO |
| | | |
| | | dn: ds-cfg-backend-id=unindexedRoot,cn=Backends,cn=config |
| | | changetype: add |
| | | objectClass: top |
| | | objectClass: ds-cfg-backend |
| | | objectClass: ds-cfg-je-backend |
| | | ds-cfg-backend-enabled: true |
| | | ds-cfg-backend-class: org.opends.server.backends.jeb.BackendImpl |
| | | ds-cfg-backend-id: unindexedRoot |
| | | ds-cfg-backend-writability-mode: enabled |
| | | ds-cfg-backend-base-dn: dc=unindexed,dc=jeb |
| | | ds-cfg-backend-directory: db_unindexed |
| | | ds-cfg-backend-mode: 700 |
| | | ds-cfg-backend-index-entry-limit: 1 |
| | | ds-cfg-backend-subtree-delete-size-limit: 100000 |
| | | ds-cfg-backend-preload-time-limit: 0 seconds |
| | | ds-cfg-backend-import-temp-directory: importTmp |
| | | ds-cfg-backend-import-buffer-size: 256 megabytes |
| | | ds-cfg-backend-import-queue-size: 100 |
| | | ds-cfg-backend-import-pass-size: 0 |
| | | ds-cfg-backend-import-thread-count: 8 |
| | | ds-cfg-backend-entries-compressed: false |
| | | ds-cfg-backend-deadlock-retry-limit: 10 |
| | | |
| | | dn: cn=Index,ds-cfg-backend-id=unindexedRoot,cn=Backends,cn=config |
| | | changetype: add |
| | | objectClass: top |
| | | objectClass: ds-cfg-branch |
| | | cn: Index |
| | | |
| | | dn: ds-cfg-index-attribute=mail,cn=Index,ds-cfg-backend-id=unindexedRoot,cn=Backends,cn=config |
| | | changetype: add |
| | | objectClass: top |
| | | objectClass: ds-cfg-je-index |
| | | ds-cfg-index-attribute: mail |
| | | ds-cfg-index-type: presence |
| | | ds-cfg-index-type: equality |
| | | ds-cfg-index-type: substring |
| | | ds-cfg-index-type: ordering |
| | | ds-cfg-index-type: approximate |
| | | |
| | | dn: ds-cfg-backend-id=rebuildRoot,cn=Backends,cn=config |
| | | changetype: add |
| | | objectClass: top |
| | |
| | | //db_rebuild is the third jeb backend used by the jeb rebuild test cases |
| | | String[] subDirectories = { "bak", "bin", "changelogDb", "classes", |
| | | "config", "db", "db_verify", "ldif", "lib", |
| | | "locks", "logs", "db_rebuild", |
| | | "locks", "logs", "db_rebuild", "db_unindexed", |
| | | "db_index_test" }; |
| | | for (String s : subDirectories) |
| | | { |
| | |
| | | import org.testng.annotations.Test; |
| | | |
| | | import org.opends.server.TestCaseUtils; |
| | | import static org.opends.server.util.StaticUtils.createEntry; |
| | | import org.opends.server.backends.task.Task; |
| | | import org.opends.server.backends.task.TaskBackend; |
| | | import org.opends.server.backends.task.TaskState; |
| | |
| | | "ds-privilege-name: -ldif-export", |
| | | "ds-privilege-name: -backend-backup", |
| | | "ds-privilege-name: -backend-restore", |
| | | "ds-privilege-name: -index-rebuild", |
| | | "ds-privilege-name: -unindexed-search", |
| | | "", |
| | | "dn: cn=Proxy Root,cn=Root DNs,cn=config", |
| | | "objectClass: top", |
| | |
| | | "ds-privilege-name: backend-restore", |
| | | "ds-privilege-name: proxied-auth", |
| | | "ds-privilege-name: bypass-acl", |
| | | "ds-privilege-name: index-rebuild", |
| | | "ds-privilege-name: unindexed-search", |
| | | "ds-pwp-password-policy-dn: cn=Clear UserPassword Policy," + |
| | | "cn=Password Policies,cn=config", |
| | | "", |
| | |
| | | connections[i] = connList.get(i); |
| | | successful[i] = successList.get(i); |
| | | } |
| | | |
| | | TestCaseUtils.addEntries( |
| | | "dn: dc=unindexed,dc=jeb", |
| | | "objectClass: top", |
| | | "objectClass: domain", |
| | | "", |
| | | "dn: cn=test1 user,dc=unindexed,dc=jeb", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "cn: test1 user", |
| | | "givenName: user", |
| | | "sn: test1", |
| | | "", |
| | | "dn: cn=test2 user,dc=unindexed,dc=jeb", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "cn: test2 user", |
| | | "givenName: user", |
| | | "sn: test2" |
| | | ); |
| | | } |
| | | |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Tests to ensure that unindexed search operations properly respect the |
| | | * UNINDEXED_SEARCH privilege. |
| | | * |
| | | * @param conn The client connection to use to perform the search operation. |
| | | * |
| | | * @param hasPrivilege Indicates whether the authenticated user is expected |
| | | * to have the UNINDEXED_SEARCH privilege and therefore |
| | | * the search should succeed. |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @Test(dataProvider = "testdata") |
| | | public void testUnindexedSearch(InternalClientConnection conn, |
| | | boolean hasPrivilege) |
| | | throws Exception |
| | | { |
| | | assertEquals(conn.hasPrivilege(Privilege.UNINDEXED_SEARCH, null), hasPrivilege); |
| | | |
| | | for(DN dn : DirectoryServer.getBaseDNs().keySet()) |
| | | { |
| | | System.out.println(dn.toString()); |
| | | } |
| | | |
| | | InternalSearchOperation searchOperation = |
| | | conn.processSearch(DN.decode("dc=unindexed,dc=jeb"), SearchScope.WHOLE_SUBTREE, |
| | | SearchFilter.createFilterFromString("(sn=test*)")); |
| | | if (hasPrivilege) |
| | | { |
| | | assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS); |
| | | } |
| | | else |
| | | { |
| | | assertEquals(searchOperation.getResultCode(), |
| | | ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Test to ensure that attempts to rebuild indexes will property respect |
| | | * the INDEX_REBUILD privilege. |
| | | * |
| | | * @param conn The client connection to use to perform the rebuild. |
| | | * @param hasPrivilege Indicates weather the authenticated user is |
| | | * expected to have the INDEX_REBUILD privilege |
| | | * and therefore the rebuild should succeed. |
| | | * @throws Exception if an unexpected problem occurs. |
| | | */ |
| | | @Test(dataProvider = "testdata", groups = { "slow" }) |
| | | public void testRebuildIndex(InternalClientConnection conn, |
| | | boolean hasPrivilege) |
| | | throws Exception |
| | | { |
| | | assertEquals(conn.hasPrivilege(Privilege.INDEX_REBUILD, null), hasPrivilege); |
| | | |
| | | Entry taskEntry = TestCaseUtils.makeEntry( |
| | | "dn: ds-task-id=" + UUID.randomUUID() + ",cn=Scheduled Tasks,cn=Tasks", |
| | | "objectclass: top", |
| | | "objectclass: ds-task", |
| | | "objectclass: ds-task-rebuild", |
| | | "ds-task-class-name: org.opends.server.tasks.RebuildTask", |
| | | "ds-task-rebuild-base-dn: dc=example,dc=com", |
| | | "ds-task-rebuild-index: cn"); |
| | | |
| | | AddOperation addOperation = |
| | | conn.processAdd(taskEntry.getDN(), taskEntry.getObjectClasses(), |
| | | taskEntry.getUserAttributes(), |
| | | taskEntry.getOperationalAttributes()); |
| | | |
| | | if (hasPrivilege) |
| | | { |
| | | assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS); |
| | | |
| | | Task task = getCompletedTask(taskEntry.getDN()); |
| | | assertNotNull(task); |
| | | assertTrue(TaskState.isSuccessful(task.getTaskState())); |
| | | } |
| | | else |
| | | { |
| | | assertEquals(addOperation.getResultCode(), |
| | | ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |