diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/QueriesImpl.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/QueriesImpl.java index 654833a6cd..43c4598e1f 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/impl/QueriesImpl.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/QueriesImpl.java @@ -2,23 +2,23 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2025 Alfresco Software Limited * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: - * + * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . * #L% @@ -38,11 +38,19 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.extensions.surf.util.I18NUtil; import org.alfresco.model.ContentModel; import org.alfresco.query.PagingRequest; +import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.site.SiteModel; import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.People; @@ -77,8 +85,6 @@ import org.alfresco.util.AlfrescoCollator; import org.alfresco.util.ISO9075; import org.alfresco.util.ParameterCheck; import org.alfresco.util.SearchLanguageConversion; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.extensions.surf.util.I18NUtil; /** * Queries implementation @@ -88,43 +94,43 @@ import org.springframework.extensions.surf.util.I18NUtil; */ public class QueriesImpl implements Queries, InitializingBean { - private final static Map NODE_SORT_PARAMS_TO_QNAMES = sortParamsToQNames( - PARAM_NAME, ContentModel.PROP_NAME, - PARAM_CREATEDAT, ContentModel.PROP_CREATED, - PARAM_MODIFIEDAT, ContentModel.PROP_MODIFIED); + private static final Log LOGGER = LogFactory.getLog(QueriesImpl.class); + private final static Map NODE_SORT_PARAMS_TO_QNAMES = sortParamsToQNames( + PARAM_NAME, ContentModel.PROP_NAME, + PARAM_CREATEDAT, ContentModel.PROP_CREATED, + PARAM_MODIFIEDAT, ContentModel.PROP_MODIFIED); private final static Map PEOPLE_SORT_PARAMS_TO_QNAMES = sortParamsToQNames( - PARAM_PERSON_ID, ContentModel.PROP_USERNAME, - ContentModel.PROP_FIRSTNAME, - ContentModel.PROP_LASTNAME); + PARAM_PERSON_ID, ContentModel.PROP_USERNAME, + ContentModel.PROP_FIRSTNAME, + ContentModel.PROP_LASTNAME); private final static Map SITE_SORT_PARAMS_TO_QNAMES = sortParamsToQNames( - PARAM_SITE_ID, ContentModel.PROP_NAME, - PARAM_SITE_TITLE, ContentModel.PROP_TITLE, + PARAM_SITE_ID, ContentModel.PROP_NAME, + PARAM_SITE_TITLE, ContentModel.PROP_TITLE, PARAM_SITE_DESCRIPTION, ContentModel.PROP_DESCRIPTION); /** - * Helper method to build a map of sort parameter names to QNames. This method iterates through - * the parameters. If a parameter is a String it is assumed to be a sort parameter name and will - * be followed by a QName to which it maps. If however it is a QName the local name of the OName - * is used as the sort parameter name. - * @param parameters to build up the map. + * Helper method to build a map of sort parameter names to QNames. This method iterates through the parameters. If a parameter is a String it is assumed to be a sort parameter name and will be followed by a QName to which it maps. If however it is a QName the local name of the OName is used as the sort parameter name. + * + * @param parameters + * to build up the map. * @return the map */ private static Map sortParamsToQNames(Object... parameters) { Map map = new HashMap<>(); - for (int i=0; i findNodes(Parameters parameters) { SearchService searchService = sr.getSearchService(); - return new AbstractQuery(nodeService, searchService) - { + return new AbstractQuery(nodeService, searchService) { private final Map mapUserInfo = new HashMap<>(10); - + @Override protected void buildQuery(StringBuilder query, String term, SearchParameters sp, String queryTemplateName) { @@ -198,14 +203,14 @@ public class QueriesImpl implements Queries, InitializingBean { query.append(")"); } - + String nodeTypeStr = parameters.getParameter(PARAM_NODE_TYPE); if (nodeTypeStr != null) { QName filterNodeTypeQName = nodes.createQName(nodeTypeStr); if (dictionaryService.getType(filterNodeTypeQName) == null) { - throw new InvalidArgumentException("Unknown filter nodeType: "+nodeTypeStr); + throw new InvalidArgumentException("Unknown filter nodeType: " + nodeTypeStr); } query.append(" AND (+TYPE:\"").append(nodeTypeStr).append(("\")")); @@ -229,7 +234,7 @@ public class QueriesImpl implements Queries, InitializingBean Path path = null; try { - path = nodeService.getPath(nodeRef); + path = nodeService.getPath(nodeRef); } catch (InvalidNodeRefException inre) { @@ -248,7 +253,7 @@ public class QueriesImpl implements Queries, InitializingBean { // first request for this namespace prefix, get and cache result Collection prefixes = namespaceService.getPrefixes(qname.getNamespaceURI()); - prefix = prefixes.size() != 0 ? prefixes.iterator().next() : ""; + prefix = !prefixes.isEmpty() ? prefixes.iterator().next() : ""; cache.put(qname.getNamespaceURI(), prefix); } buf.append('/').append(prefix).append(':').append(ISO9075.encode(qname.getLocalName())); @@ -262,12 +267,6 @@ public class QueriesImpl implements Queries, InitializingBean return buf.toString(); } - @Override - protected List newList(int capacity) - { - return new ArrayList(capacity); - } - @Override protected Node convert(NodeRef nodeRef, List includeParam) { @@ -281,18 +280,17 @@ public class QueriesImpl implements Queries, InitializingBean term = SearchLanguageConversion.escapeLuceneQuery(term); return term; } - + }.find(parameters, PARAM_TERM, MIN_TERM_LENGTH_NODES, "keywords", - IN_QUERY_SORT, NODE_SORT_PARAMS_TO_QNAMES, - new SortColumn(PARAM_MODIFIEDAT, false)); + IN_QUERY_SORT, NODE_SORT_PARAMS_TO_QNAMES, + new SortColumn(PARAM_MODIFIEDAT, false)); } - + @Override public CollectionWithPagingInfo findPeople(Parameters parameters) { SearchService searchService = sr.getSearchService(); - return new AbstractQuery(nodeService, searchService) - { + return new AbstractQuery(nodeService, searchService) { @Override protected void buildQuery(StringBuilder query, String term, SearchParameters sp, String queryTemplateName) { @@ -305,33 +303,25 @@ public class QueriesImpl implements Queries, InitializingBean query.append("*\")"); } - @Override - protected List newList(int capacity) - { - return new ArrayList(capacity); - } - @Override protected Person convert(NodeRef nodeRef, List includeParam) { String personId = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME); - Person person = people.getPerson(personId); - return person; + return people.getPerson(personId); } - + // TODO Do the sort in the query on day. A comment in the code for the V0 API used for live people - // search says adding sort values for this query don't work - tried it and they really don't. + // search says adding sort values for this query don't work - tried it and they really don't. }.find(parameters, PARAM_TERM, MIN_TERM_LENGTH_PEOPLE, "_PERSON", - POST_QUERY_SORT, PEOPLE_SORT_PARAMS_TO_QNAMES, - new SortColumn(PARAM_FIRSTNAME, true), new SortColumn(PARAM_LASTNAME, true)); + POST_QUERY_SORT, PEOPLE_SORT_PARAMS_TO_QNAMES, + new SortColumn(PARAM_FIRSTNAME, true), new SortColumn(PARAM_LASTNAME, true)); } @Override public CollectionWithPagingInfo findSites(Parameters parameters) { SearchService searchService = sr.getSearchService(); - return new AbstractQuery(nodeService, searchService) - { + return new AbstractQuery(nodeService, searchService) { @Override protected void buildQuery(StringBuilder query, String term, SearchParameters sp, String queryTemplateName) { @@ -343,44 +333,34 @@ public class QueriesImpl implements Queries, InitializingBean query.append(term); query.append("*\")"); } - - @Override - protected List newList(int capacity) - { - return new ArrayList<>(capacity); - } @Override protected Site convert(NodeRef nodeRef, List includeParam) { - return getSite(siteService.getSite(nodeRef), true); + return getSite(siteService.getSite(nodeRef)); } // note: see also Sites.getSite - private Site getSite(SiteInfo siteInfo, boolean includeRole) + private Site getSite(SiteInfo siteInfo) { // set the site id to the short name (to deal with case sensitivity issues with using the siteId from the url) String siteId = siteInfo.getShortName(); - String role = null; - if(includeRole) - { - role = sites.getSiteRole(siteId); - } + String role = sites.getSiteRole(siteId); return new Site(siteInfo, role); } }.find(parameters, PARAM_TERM, MIN_TERM_LENGTH_SITES, "_SITE", POST_QUERY_SORT, SITE_SORT_PARAMS_TO_QNAMES, new SortColumn(PARAM_SITE_TITLE, true)); } - + public abstract static class AbstractQuery { public enum Sort { IN_QUERY_SORT, POST_QUERY_SORT } - + private final NodeService nodeService; private final SearchService searchService; - + public AbstractQuery(NodeService nodeService, SearchService searchService) { this.nodeService = nodeService; @@ -388,14 +368,14 @@ public class QueriesImpl implements Queries, InitializingBean } public CollectionWithPagingInfo find(Parameters parameters, - String termName, int minTermLength, String queryTemplateName, - Sort sort, Map sortParamsToQNames, SortColumn... defaultSort) + String termName, int minTermLength, String queryTemplateName, + Sort sort, Map sortParamsToQNames, SortColumn... defaultSort) { SearchParameters sp = new SearchParameters(); sp.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO); sp.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); sp.setDefaultFieldName(queryTemplateName); - + String term = getTerm(parameters, termName, minTermLength); StringBuilder query = new StringBuilder(); @@ -404,7 +384,7 @@ public class QueriesImpl implements Queries, InitializingBean Paging paging = parameters.getPaging(); PagingRequest pagingRequest = Util.getPagingRequest(paging); - + List defaultSortCols = (defaultSort != null ? Arrays.asList(defaultSort) : Collections.emptyList()); if (sort == IN_QUERY_SORT) { @@ -413,36 +393,43 @@ public class QueriesImpl implements Queries, InitializingBean sp.setSkipCount(pagingRequest.getSkipCount()); sp.setMaxItems(pagingRequest.getMaxItems()); } - + ResultSet queryResults = null; - List collection = null; try { queryResults = searchService.query(sp); - + List nodeRefs = queryResults.getNodeRefs(); - - if (sort == POST_QUERY_SORT) - { - nodeRefs = postQuerySort(parameters, sortParamsToQNames, defaultSortCols, nodeRefs); - } - - collection = newList(nodeRefs.size()); + + Map + + collection = new LinkedHashMap<>(nodeRefs.size()); List includeParam = parameters.getInclude(); for (NodeRef nodeRef : nodeRefs) { - T t = convert(nodeRef, includeParam); - collection.add(t); + try + { + T t = convert(nodeRef, includeParam); + collection.put(nodeRef, t); + } + catch (AccessDeniedException ade) + { + LOGGER.debug("Ignoring search result for nodeRef " + nodeRef + " due to access denied exception", ade); + } } if (sort == POST_QUERY_SORT) { - return listPage(collection, paging); + List postQuerySortedCollection = postQuerySort(parameters, sortParamsToQNames, defaultSortCols, collection.keySet()) + .stream() + .map(collection::get) + .toList(); + return listPage(postQuerySortedCollection, paging); } else { - return CollectionWithPagingInfo.asPaged(paging, collection, queryResults.hasMore(), Long.valueOf(queryResults.getNumberFound()).intValue()); + return CollectionWithPagingInfo.asPaged(paging, collection.values(), queryResults.hasMore(), Long.valueOf(queryResults.getNumberFound()).intValue()); } } finally @@ -455,40 +442,39 @@ public class QueriesImpl implements Queries, InitializingBean } /** - * Builds up the query and is expected to call {@link SearchParameters#setDefaultFieldName(String)} - * and {@link SearchParameters#addQueryTemplate(String, String)} - * @param query StringBuilder into which the query should be built. - * @param term to be searched for - * @param sp SearchParameters + * Builds up the query and is expected to call {@link SearchParameters#setDefaultFieldName(String)} and {@link SearchParameters#addQueryTemplate(String, String)} + * + * @param query + * StringBuilder into which the query should be built. + * @param term + * to be searched for + * @param sp + * SearchParameters * @param queryTemplateName */ protected abstract void buildQuery(StringBuilder query, String term, SearchParameters sp, String queryTemplateName); - - /** - * Returns a list of the correct type. - * @param capacity of the list - * @return a new list. - */ - protected abstract List newList(int capacity); /** * Converts a nodeRef into the an object of the required type. - * @param nodeRef to be converted - * @param includeParam additional fields to be included + * + * @param nodeRef + * to be converted + * @param includeParam + * additional fields to be included * @return the object */ protected abstract T convert(NodeRef nodeRef, List includeParam); - + protected String getTerm(Parameters parameters, String termName, int minTermLength) { String term = parameters.getParameter(termName); if (term == null) { - throw new InvalidArgumentException("Query '"+termName+"' not specified"); + throw new InvalidArgumentException("Query '" + termName + "' not specified"); } - + term = escapeTerm(term); - + int cnt = 0; for (int i = 0; i < term.length(); i++) { @@ -505,7 +491,7 @@ public class QueriesImpl implements Queries, InitializingBean if (cnt < minTermLength) { - throw new InvalidArgumentException("Query '"+termName+"' is too short. Must have at least "+minTermLength+" alphanumeric chars"); + throw new InvalidArgumentException("Query '" + termName + "' is too short. Must have at least " + minTermLength + " alphanumeric chars"); } return term; @@ -524,12 +510,12 @@ public class QueriesImpl implements Queries, InitializingBean term = SearchLanguageConversion.escapeLuceneQuery(term); return term; } - + /** * Adds sort order to the SearchParameters. */ protected void addSortOrder(Parameters parameters, Map sortParamsToQNames, - List defaultSortCols, SearchParameters sp) + List defaultSortCols, SearchParameters sp) { List sortCols = getSorting(parameters, defaultSortCols); for (SortColumn sortCol : sortCols) @@ -537,16 +523,16 @@ public class QueriesImpl implements Queries, InitializingBean QName sortPropQName = sortParamsToQNames.get(sortCol.column); if (sortPropQName == null) { - throw new InvalidArgumentException("Invalid sort field: "+sortCol.column); + throw new InvalidArgumentException("Invalid sort field: " + sortCol.column); } - sp.addSort("@" + sortPropQName, sortCol.asc); + sp.addSort("@" + sortPropQName, sortCol.asc); } } private List getSorting(Parameters parameters, List defaultSortCols) { List sortCols = parameters.getSorting(); - if (sortCols == null || sortCols.size() == 0) + if (sortCols == null || sortCols.isEmpty()) { sortCols = defaultSortCols == null ? Collections.emptyList() : defaultSortCols; } @@ -554,63 +540,66 @@ public class QueriesImpl implements Queries, InitializingBean } protected List postQuerySort(Parameters parameters, Map sortParamsToQNames, - List defaultSortCols, List nodeRefs) + List defaultSortCols, Set unsortedNodeRefs) { final List sortCols = getSorting(parameters, defaultSortCols); int sortColCount = sortCols.size(); - if (sortColCount > 0) - { - // make copy of nodeRefs because it can be unmodifiable list. - nodeRefs = new ArrayList(nodeRefs); - - List sortPropQNames = new ArrayList<>(sortColCount); - for (SortColumn sortCol : sortCols) - { - QName sortPropQName = sortParamsToQNames.get(sortCol.column); - if (sortPropQName == null) - { - throw new InvalidArgumentException("Invalid sort field: "+sortCol.column); - } - sortPropQNames.add(sortPropQName); - } - - final Collator col = AlfrescoCollator.getInstance(I18NUtil.getLocale()); - Collections.sort(nodeRefs, new Comparator() - { - @Override - public int compare(NodeRef n1, NodeRef n2) - { - int result = 0; - for (int i=0; i(unsortedNodeRefs); + } + + // make copy of nodeRefs because it can be unmodifiable list. + List sortedNodeRefs = new ArrayList<>(unsortedNodeRefs); + + List sortPropQNames = new ArrayList<>(sortColCount); + for (SortColumn sortCol : sortCols) + { + QName sortPropQName = sortParamsToQNames.get(sortCol.column); + if (sortPropQName == null) + { + throw new InvalidArgumentException("Invalid sort field: " + sortCol.column); + } + sortPropQNames.add(sortPropQName); + } + + final Collator col = AlfrescoCollator.getInstance(I18NUtil.getLocale()); + Collections.sort(sortedNodeRefs, new Comparator() { + @Override + public int compare(NodeRef n1, NodeRef n2) + { + int result = 0; + for (int i = 0; i < sortCols.size(); i++) + { + SortColumn sortCol = sortCols.get(i); + QName sortPropQName = sortPropQNames.get(i); + + Serializable p1 = getProperty(n1, sortPropQName); + Serializable p2 = getProperty(n2, sortPropQName); + + result = ((p1 instanceof Long) && (p2 instanceof Long) + ? Long.compare((Long) p1, (Long) p2) : col.compare(p1.toString(), p2.toString())) * (sortCol.asc ? 1 : -1); - - if (result != 0) - { - break; - } + + if (result != 0) + { + break; } - return result; } + return result; + } - private Serializable getProperty(NodeRef nodeRef, QName sortPropQName) - { - Serializable result = nodeService.getProperty(nodeRef, sortPropQName); - return result == null ? "" : result; - } + private Serializable getProperty(NodeRef nodeRef, QName sortPropQName) + { + Serializable result = nodeService.getProperty(nodeRef, sortPropQName); + return result == null ? "" : result; + } - }); - } - return nodeRefs; + }); + + return sortedNodeRefs; } // note: see also AbstractNodeRelation diff --git a/remote-api/src/test/java/org/alfresco/rest/api/tests/QueriesSitesApiTest.java b/remote-api/src/test/java/org/alfresco/rest/api/tests/QueriesSitesApiTest.java index 807f44bbf7..197759e669 100644 --- a/remote-api/src/test/java/org/alfresco/rest/api/tests/QueriesSitesApiTest.java +++ b/remote-api/src/test/java/org/alfresco/rest/api/tests/QueriesSitesApiTest.java @@ -25,6 +25,20 @@ */ package org.alfresco.rest.api.tests; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.rest.AbstractSingleNetworkSiteTest; import org.alfresco.rest.api.Queries; @@ -36,27 +50,12 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteVisibility; -import org.alfresco.util.testing.category.LuceneTests; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.mockito.ArgumentCaptor; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; /** -* V1 REST API tests for pre-defined 'live' search Queries on Sites + * V1 REST API tests for pre-defined 'live' search Queries on Sites * *
    - *
  • {@literal :/alfresco/api//public/alfresco/versions/1/queries/sites}
  • + *
  • {@literal :/alfresco/api//public/alfresco/versions/1/queries/sites}
  • *
* * @author janv @@ -64,7 +63,7 @@ import static org.mockito.Mockito.verify; public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest { private static final String URL_QUERIES_LSS = "queries/sites"; - + private SiteService siteService; @Before @@ -72,7 +71,7 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest public void setup() throws Exception { super.setup(); - siteService = (SiteService)applicationContext.getBean("SiteService"); + siteService = (SiteService) applicationContext.getBean("SiteService"); } // Note expectedIds defaults to ids @@ -86,7 +85,7 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest } dummySearchServiceQueryNodeRefs.clear(); - for (String id: ids) + for (String id : ids) { NodeRef nodeRef = getNodeRef(id); dummySearchServiceQueryNodeRefs.add(nodeRef); @@ -98,7 +97,7 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest if (expectedStatus == 200) { String termWithEscapedAsterisks = term.replaceAll("\\*", "\\\\*"); - String expectedQuery = "TYPE:\"{http://www.alfresco.org/model/site/1.0}site\" AND (\"*"+ termWithEscapedAsterisks +"*\")"; + String expectedQuery = "TYPE:\"{http://www.alfresco.org/model/site/1.0}site\" AND (\"*" + termWithEscapedAsterisks + "*\")"; ArgumentCaptor searchParametersCaptor = ArgumentCaptor.forClass(SearchParameters.class); verify(mockSearchService, times(++callCountToMockSearchService)).query(searchParametersCaptor.capture()); SearchParameters parameters = searchParametersCaptor.getValue(); @@ -109,30 +108,32 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest if (orderBy != null) { - for (int i=0; iGET:

+ *

+ * GET: + *

* {@literal :/alfresco/api//public/alfresco/versions/1/queries/sites} */ @Test public void testLiveSearchSites() throws Exception { setRequestContext(user1); - + AuthenticationUtil.setFullyAuthenticatedUser(user1); int sCount = 5; assertTrue(sCount > 4); // as relied on by test below - + List siteIds = new ArrayList<>(sCount); try @@ -149,14 +150,14 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest String siteD = "siteD"; int charValue = siteI.charAt(0); - + // create some some sites with site id: ab00001, abc00002, abcd00003, abcde00004, abcdef00005 (and some specific titles and descriptions) for (int i = 1; i <= sCount; i++) { String num = String.format("%05d", i); - charValue = charValue+1; - siteI = siteI + String.valueOf((char)charValue); + charValue = charValue + 1; + siteI = siteI + String.valueOf((char) charValue); String siteId = siteI + num + RUNID; String siteTitle = siteT + num + siteT; @@ -220,7 +221,7 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest { // some cleanup setRequestContext(user1); - + for (String siteId : siteIds) { deleteSite(siteId, true, 204); @@ -230,14 +231,20 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest private NodeRef getNodeRef(String createdSiteId) { - AuthenticationUtil.setFullyAuthenticatedUser(user1); + // Created sites do not return NodeRefs to the caller so we need to get the NodeRef from the siteService. + // Temporarily as admin we will get NodeRefs to handle ACL authorization. + String userUnderTest = AuthenticationUtil.getFullyAuthenticatedUser(); + AuthenticationUtil.setFullyAuthenticatedUser(DEFAULT_ADMIN); + // The following call to siteService.getSite(createdSiteId).getNodeRef() returns a NodeRef like: - // workspace://SpacesStore/9db76769-96de-4de4-bdb4-a127130af362 + // workspace://SpacesStore/9db76769-96de-4de4-bdb4-a127130af362 // We call tenantService.getName(nodeRef) to get a fully qualified NodeRef as Solr returns this. // They look like: - // workspace://@org.alfresco.rest.api.tests.queriespeopleapitest@SpacesStore/9db76769-96de-4de4-bdb4-a127130af362 + // workspace://@org.alfresco.rest.api.tests.queriespeopleapitest@SpacesStore/9db76769-96de-4de4-bdb4-a127130af362 NodeRef nodeRef = siteService.getSite(createdSiteId).getNodeRef(); nodeRef = tenantService.getName(nodeRef); + + AuthenticationUtil.setFullyAuthenticatedUser(userUnderTest); return nodeRef; } @@ -245,7 +252,7 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest public void testLiveSearchSites_SortPage() throws Exception { setRequestContext(user1); - + AuthenticationUtil.setFullyAuthenticatedUser(user1); List siteIds = new ArrayList<>(5); try @@ -253,14 +260,14 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest // As user 1 ... Paging paging = getPaging(0, 100); - + // create site - String s1 = createSite("siABCDEF"+RUNID, "ABCDEF DEF", "sdABCDEF", SiteVisibility.PRIVATE, 201).getId(); - String s2 = createSite("siABCD"+RUNID, "ABCD DEF", "sdABCD", SiteVisibility.PRIVATE, 201).getId(); - String s3 = createSite("siABCDE"+RUNID, "ABCDE DEF", "sdABCDE", SiteVisibility.PRIVATE, 201).getId(); - String s4 = createSite("siAB"+RUNID, "AB DEF", "sdAB", SiteVisibility.PRIVATE, 201).getId(); - String s5 = createSite("siABC"+RUNID, "ABC DEF", "sdABC", SiteVisibility.PRIVATE, 201).getId(); - + String s1 = createSite("siABCDEF" + RUNID, "ABCDEF DEF", "sdABCDEF", SiteVisibility.PRIVATE, 201).getId(); + String s2 = createSite("siABCD" + RUNID, "ABCD DEF", "sdABCD", SiteVisibility.PRIVATE, 201).getId(); + String s3 = createSite("siABCDE" + RUNID, "ABCDE DEF", "sdABCDE", SiteVisibility.PRIVATE, 201).getId(); + String s4 = createSite("siAB" + RUNID, "AB DEF", "sdAB", SiteVisibility.PRIVATE, 201).getId(); + String s5 = createSite("siABC" + RUNID, "ABC DEF", "sdABC", SiteVisibility.PRIVATE, 201).getId(); + // test sort order { // default sort order - title asc @@ -276,11 +283,11 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest // basic paging tests { // sort order - title desc - checkApiCall("siAB", "title desc", getPaging(0, 2), 200, new String[] {s1, s3}, s1, s3, s2, s5, s4); - checkApiCall("siAB", "title desc", getPaging(2, 2), 200, new String[] {s2, s5}, s1, s3, s2, s5, s4); - checkApiCall("siAB", "title desc", getPaging(4, 2), 200, new String[] {s4}, s1, s3, s2, s5, s4); + checkApiCall("siAB", "title desc", getPaging(0, 2), 200, new String[]{s1, s3}, s1, s3, s2, s5, s4); + checkApiCall("siAB", "title desc", getPaging(2, 2), 200, new String[]{s2, s5}, s1, s3, s2, s5, s4); + checkApiCall("siAB", "title desc", getPaging(4, 2), 200, new String[]{s4}, s1, s3, s2, s5, s4); } - + // -ve tests { // -ve test - invalid sort field @@ -304,7 +311,52 @@ public class QueriesSitesApiTest extends AbstractSingleNetworkSiteTest } } } - + + /** + * If the search service do not support ACL filtering, then the Queries API should handle the response to exclude private sites and potential unauthorized error when building response. + */ + @Test + public void testLiveSearchExcludesPrivateSites() throws Exception + { + String publicSiteId = null; + String privateSiteId = null; + try + { + // given + setRequestContext(null, DEFAULT_ADMIN, DEFAULT_ADMIN_PWD); + createUser("bartender"); + + publicSiteId = createSite("samePrefixPublicSite", "samePrefixPublicSite", "Visible to all users", SiteVisibility.PUBLIC, 201).getId(); + privateSiteId = createSite("samePrefixPrivateSite", "samePrefixPrivateSite", "Hidden from bartender", SiteVisibility.PRIVATE, 201).getId(); + + String[] searchResults = {publicSiteId, privateSiteId}; + String[] expectedSites = {publicSiteId}; + + // when + setRequestContext(null, "bartender", "password"); + AuthenticationUtil.setFullyAuthenticatedUser("bartender"); + + // then + checkApiCall("samePrefix", null, getPaging(0, 100), 200, expectedSites, searchResults); + } + finally + { + // cleanup + AuthenticationUtil.setFullyAuthenticatedUser(DEFAULT_ADMIN); + setRequestContext(null, DEFAULT_ADMIN, DEFAULT_ADMIN_PWD); + if (publicSiteId != null) + { + deleteSite(publicSiteId, true, 204); + } + if (privateSiteId != null) + { + deleteSite(privateSiteId, true, 204); + } + deleteUser("bartender", null); + } + + } + @Override public String getScope() {