/* * 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 2008-2009 Sun Microsystems, Inc. * Portions Copyright 2014-2015 ForgeRock AS */ package org.opends.server.controls; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.SearchScope; import org.opends.server.TestCaseUtils; import org.opends.server.core.DirectoryServer; import org.opends.server.protocols.internal.InternalSearchOperation; import org.opends.server.protocols.internal.SearchRequest; import org.opends.server.protocols.ldap.LDAPControl; import org.opends.server.types.AttributeType; import org.opends.server.types.Control; import org.opends.server.types.DN; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; import org.opends.server.types.SearchResultEntry; import org.opends.server.types.SortKey; import org.opends.server.types.SortOrder; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import static org.opends.server.TestCaseUtils.*; import static org.opends.server.protocols.internal.InternalClientConnection.*; import static org.opends.server.protocols.internal.Requests.*; import static org.testng.Assert.*; /** * This class contains a number of test cases for the server side sort request * and response controls. */ public class ServerSideSortControlTestCase extends ControlsTestCase { /** The givenName attribute type. */ private AttributeType givenNameType; /** The sn attribute type. */ private AttributeType snType; /** The DN for "Aaccf Johnson". */ DN aaccfJohnsonDN; /** The DN for "Aaron Zimmerman". */ DN aaronZimmermanDN; /** The DN for "Albert Smith". */ DN albertSmithDN; /** The DN for "Albert Zimmerman". */ DN albertZimmermanDN; /** The DN for "lowercase mcgee". */ DN lowercaseMcGeeDN; /** The DN for "Mararet Jones". */ DN margaretJonesDN; /** The DN for "Mary Jones". */ DN maryJonesDN; /** The DN for "Sam Zweck". */ DN samZweckDN; /** The DN for "Zorro". */ DN zorroDN; /** * Make sure that the server is running. * * @throws Exception If an unexpected problem occurs. */ @BeforeClass public void startServer() throws Exception { TestCaseUtils.startServer(); givenNameType = DirectoryServer.getAttributeTypeOrNull("givenname"); assertNotNull(givenNameType); snType = DirectoryServer.getAttributeTypeOrNull("sn"); assertNotNull(snType); aaccfJohnsonDN = DN.valueOf("uid=aaccf.johnson,dc=example,dc=com"); aaronZimmermanDN = DN.valueOf("uid=aaron.zimmerman,dc=example,dc=com"); albertSmithDN = DN.valueOf("uid=albert.smith,dc=example,dc=com"); albertZimmermanDN = DN.valueOf("uid=albert.zimmerman,dc=example,dc=com"); lowercaseMcGeeDN = DN.valueOf("uid=lowercase.mcgee,dc=example,dc=com"); margaretJonesDN = DN.valueOf("uid=margaret.jones,dc=example,dc=com"); maryJonesDN = DN.valueOf("uid=mary.jones,dc=example,dc=com"); samZweckDN = DN.valueOf("uid=sam.zweck,dc=example,dc=com"); zorroDN = DN.valueOf("uid=zorro,dc=example,dc=com"); } /** * Populates the JE DB with a set of test data. * * @throws Exception If an unexpected problem occurs. */ private void populateDB() throws Exception { TestCaseUtils.clearBackend("userRoot", "dc=example,dc=com"); TestCaseUtils.addEntries( "dn: uid=albert.zimmerman,dc=example,dc=com", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "uid: albert.zimmerman", "givenName: Albert", "sn: Zimmerman", "cn: Albert Zimmerman", "", "dn: uid=albert.smith,dc=example,dc=com", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "uid: albert.smith", "givenName: Albert", "sn: Smith", "cn: Albert Smith", "", "dn: uid=aaron.zimmerman,dc=example,dc=com", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "uid: albert.zimmerman", "givenName: Aaron", "givenName: Zeke", "sn: Zimmerman", "cn: Aaron Zimmerman", "", "dn: uid=mary.jones,dc=example,dc=com", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "uid: mary.jones", "givenName: Mary", "sn: Jones", "cn: Mary Jones", "", "dn: uid=margaret.jones,dc=example,dc=com", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "uid: margaret.jones", "givenName: Margaret", "givenName: Maggie", "sn: Jones", "sn: Smith", "cn: Maggie Jones-Smith", "", "dn: uid=aaccf.johnson,dc=example,dc=com", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "uid: aaccf.johnson", "givenName: Aaccf", "sn: Johnson", "cn: Aaccf Johnson", "", "dn: uid=sam.zweck,dc=example,dc=com", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "uid: sam.zweck", "givenName: Sam", "sn: Zweck", "cn: Sam Zweck", "", "dn: uid=lowercase.mcgee,dc=example,dc=com", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "uid: lowercase.mcgee", "givenName: lowercase", "sn: mcgee", "cn: lowercase mcgee", "", "dn: uid=zorro,dc=example,dc=com", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "uid: zorro", "sn: Zorro", "cn: Zorro" ); } /** * Tests the first constructor for the request control with different sort * order values. * * @throws Exception If an unexpected problem occurs. */ @Test public void testRequestConstructor1() throws Exception { SortKey sortKey = new SortKey(givenNameType, true); SortOrder sortOrder = new SortOrder(sortKey); new ServerSideSortRequestControl(sortOrder).toString(); sortKey.toString(); sortOrder.toString(); sortKey = new SortKey(givenNameType, false); sortOrder = new SortOrder(sortKey); new ServerSideSortRequestControl(sortOrder).toString(); sortKey.toString(); sortOrder.toString(); sortOrder = new SortOrder( new SortKey(snType, true), new SortKey(givenNameType, true)); new ServerSideSortRequestControl(sortOrder).toString(); sortOrder.toString(); } /** * Tests the second constructor for the request control with different sort * order strings. * * @throws Exception If an unexpected problem occurs. */ @Test public void testRequestConstructor2() throws Exception { new ServerSideSortRequestControl("givenName").toString(); new ServerSideSortRequestControl("givenName:caseIgnoreOrderingMatch").toString(); new ServerSideSortRequestControl("+givenName").toString(); new ServerSideSortRequestControl("-givenName").toString(); new ServerSideSortRequestControl("givenName,sn").toString(); new ServerSideSortRequestControl("givenName,+sn").toString(); new ServerSideSortRequestControl("givenName,-sn").toString(); new ServerSideSortRequestControl("+givenName,sn").toString(); new ServerSideSortRequestControl("+givenName,+sn").toString(); new ServerSideSortRequestControl("+givenName,-sn").toString(); new ServerSideSortRequestControl("-givenName").toString(); new ServerSideSortRequestControl("-givenName,+sn").toString(); new ServerSideSortRequestControl("-givenName,-sn").toString(); new ServerSideSortRequestControl("-givenName,-sn:caseExactOrderingMatch").toString(); } /** * Tests performing an internal search using the server-side sort control to * sort the entries in order of ascending givenName values. * * @throws Exception If an unexpected problem occurred. */ @Test public void testInternalSearchGivenNameAscending() throws Exception { testInternalSearchWithSort("givenName", aaccfJohnsonDN, // Aaccf aaronZimmermanDN, // Aaron albertZimmermanDN, // Albert, lower entry ID albertSmithDN, // Albert, higher entry ID lowercaseMcGeeDN, // lowercase margaretJonesDN, // Maggie maryJonesDN, // Mary samZweckDN, // Sam zorroDN); // No first name } /** * Tests performing an internal search using the server-side sort control to * sort the entries in order of ascending givenName values using a specific * ordering matching rule. * * @throws Exception If an unexpected problem occurred. */ @Test public void testInternalSearchGivenNameAscendingCaseExact() throws Exception { testInternalSearchWithSort("givenName:caseExactOrderingMatch", aaccfJohnsonDN, // Aaccf aaronZimmermanDN, // Aaron albertZimmermanDN, // Albert, lower entry ID albertSmithDN, // Albert, higher entry ID margaretJonesDN, // Maggie maryJonesDN, // Mary samZweckDN, // Sam lowercaseMcGeeDN, // lowercase zorroDN); // No first name } /** * Tests performing an internal search using the server-side sort control to * sort the entries in order of descending givenName values. * * @throws Exception If an unexpected problem occurred. */ @Test public void testInternalSearchGivenNameDescending() throws Exception { testInternalSearchWithSort("-givenName", zorroDN, // No first name samZweckDN, // Sam maryJonesDN, // Mary margaretJonesDN, // Margaret lowercaseMcGeeDN, // lowercase albertZimmermanDN, // Albert, lower entry ID albertSmithDN, // Albert, higher entry ID aaronZimmermanDN, // Aaron aaccfJohnsonDN); // Aaccf } /** * Tests performing an internal search using the server-side sort control to * sort the entries in order of descending givenName values using a specific * ordering matching rule. * * @throws Exception If an unexpected problem occurred. */ @Test public void testInternalSearchGivenNameDescendingCaseExact() throws Exception { testInternalSearchWithSort("-givenName:caseExactOrderingMatch", zorroDN, // No first name lowercaseMcGeeDN, // lowercase samZweckDN, // Sam maryJonesDN, // Mary margaretJonesDN, // Margaret albertZimmermanDN, // Albert, lower entry ID albertSmithDN, // Albert, higher entry ID aaronZimmermanDN, // Aaron aaccfJohnsonDN); // Aaccf } /** * Tests performing an internal search using the server-side sort control to * sort the entries in order of ascending givenName and ascending sn values. * * @throws Exception If an unexpected problem occurred. */ @Test public void testInternalSearchGivenNameAscendingSnAscending() throws Exception { testInternalSearchWithSort("givenName,sn", aaccfJohnsonDN, // Aaccf aaronZimmermanDN, // Aaron albertSmithDN, // Albert, lower sn albertZimmermanDN, // Albert, higher sn lowercaseMcGeeDN, // lowercase margaretJonesDN, // Maggie maryJonesDN, // Mary samZweckDN, // Sam zorroDN); // No first name } /** * Tests performing an internal search using the server-side sort control to * sort the entries in order of ascending givenName and descending sn values. * * @throws Exception If an unexpected problem occurred. */ @Test public void testInternalSearchGivenNameAscendingSnDescending() throws Exception { testInternalSearchWithSort("givenName,-sn", aaccfJohnsonDN, // Aaccf aaronZimmermanDN, // Aaron albertZimmermanDN, // Albert, higher sn albertSmithDN, // Albert, lower sn lowercaseMcGeeDN, // lowercase margaretJonesDN, // Maggie maryJonesDN, // Mary samZweckDN, // Sam zorroDN); // No first name } private void testInternalSearchWithSort(String sortOrderString, DN... expectedDNOrder) throws Exception { populateDB(); SearchRequest request = newSearchRequest("dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(objectClass=person)") .addControl(new ServerSideSortRequestControl(sortOrderString)); InternalSearchOperation internalSearch = getRootConnection().processSearch(request); assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS); assertEquals(getDNs(internalSearch.getSearchEntries()), Arrays.asList(expectedDNOrder)); assertNoAttributeTypeForSort(internalSearch); } /** * Tests performing an internal search using the CRITICAL server-side sort control with * an undefined attribute type. * * @throws Exception If an unexpected problem occurred. */ @Test public void testCriticalSortWithUndefinedAttribute() throws Exception { populateDB(); SearchRequest request = newSearchRequest("dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(objectClass=person)") .addControl(new ServerSideSortRequestControl(true, "undefined")); InternalSearchOperation internalSearch = getRootConnection().processSearch(request); assertEquals(internalSearch.getResultCode(), ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); } /** * Tests performing an internal search using the server-side sort control with * an undefined ordering rule. * * @throws Exception If an unexpected problem occurred. */ @Test public void testInternalSearchUndefinedOrderingRule() throws Exception { populateDB(); SearchRequest request = newSearchRequest("dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(objectClass=person)") .addControl(new ServerSideSortRequestControl(true, "givenName:undefinedOrderingMatch")); InternalSearchOperation internalSearch = getRootConnection().processSearch(request); assertNotEquals(internalSearch.getResultCode(), ResultCode.SUCCESS); } /** * Tests performing an internal search using the non-critical server-side * sort control to sort the entries * * @throws Exception If an unexpected problem occurred. */ @Test public void testNonCriticalSortWithUndefinedAttribute() throws Exception { populateDB(); SearchRequest request = newSearchRequest("dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(objectClass=person)") .addControl(new ServerSideSortRequestControl(false, "bad_sort:caseExactOrderingMatch")); InternalSearchOperation internalSearch = getRootConnection().processSearch(request); assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS); ServerSideSortResponseControl responseControl = getServerSideSortResponseControl(internalSearch); assertEquals(responseControl.getResultCode(), 16); } private void assertNoAttributeTypeForSort(InternalSearchOperation internalSearch) throws Exception { ServerSideSortResponseControl responseControl = getServerSideSortResponseControl(internalSearch); assertEquals(responseControl.getResultCode(), ResultCode.SUCCESS.intValue()); assertNull(responseControl.getAttributeType()); responseControl.toString(); } private ServerSideSortResponseControl getServerSideSortResponseControl(InternalSearchOperation internalSearch) throws Exception { List responseControls = internalSearch.getResponseControls(); assertNotNull(responseControls); assertEquals(responseControls.size(), 1); return getServerSideSortResponseControl(responseControls); } private ServerSideSortResponseControl getServerSideSortResponseControl(List responseControls) throws DirectoryException { Control c = responseControls.get(0); if (c instanceof ServerSideSortResponseControl) { return (ServerSideSortResponseControl) c; } return ServerSideSortResponseControl.DECODER.decode(c.isCritical(), ((LDAPControl) c).getValue()); } private ArrayList getDNs(LinkedList searchEntries) { ArrayList results = new ArrayList<>(); for (Entry e : searchEntries) { results.add(e.getName()); } return results; } }