MNT-22385 Cmis query GetTotalNumItems is returning wrong value (#504)

* Changes made to correct the value of totalItems when performing a TMDQ

* Fixes after review

- Slight change was made to NodePermissionAssessor to log when permission
  limits are exceeded

* Now pre-computing maxPermissionChecks value as per review suggestion

(cherry picked from commit cb636d1140)
This commit is contained in:
Nana Insaidoo
2021-05-28 09:16:38 +01:00
committed by Nana Insaidoo
parent 664d0b9704
commit b9b41a10e8
5 changed files with 542 additions and 469 deletions

View File

@@ -35,6 +35,7 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.NotThreadSafe;
@@ -46,6 +47,7 @@ import org.alfresco.repo.cache.lookup.EntityLookupCache;
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor; import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor;
import org.alfresco.repo.domain.node.Node; import org.alfresco.repo.domain.node.Node;
import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.domain.node.StoreEntity;
import org.alfresco.repo.domain.permissions.AclCrudDAO; import org.alfresco.repo.domain.permissions.AclCrudDAO;
import org.alfresco.repo.domain.permissions.Authority; import org.alfresco.repo.domain.permissions.Authority;
import org.alfresco.repo.domain.qname.QNameDAO; import org.alfresco.repo.domain.qname.QNameDAO;
@@ -110,8 +112,12 @@ public class DBQueryEngine implements QueryEngine
private long maxPermissionCheckTimeMillis; private long maxPermissionCheckTimeMillis;
private boolean maxPermissionCheckEnabled;
protected EntityLookupCache<Long, Node, NodeRef> nodesCache; protected EntityLookupCache<Long, Node, NodeRef> nodesCache;
private List<Pair<Long, StoreRef>> stores;
AclCrudDAO aclCrudDAO; AclCrudDAO aclCrudDAO;
public void setAclCrudDAO(AclCrudDAO aclCrudDAO) public void setAclCrudDAO(AclCrudDAO aclCrudDAO)
@@ -129,6 +135,11 @@ public class DBQueryEngine implements QueryEngine
this.maxPermissionCheckTimeMillis = maxPermissionCheckTimeMillis; this.maxPermissionCheckTimeMillis = maxPermissionCheckTimeMillis;
} }
public void setMaxPermissionCheckEnabled(boolean maxPermissionCheckEnabled)
{
this.maxPermissionCheckEnabled = maxPermissionCheckEnabled;
}
public void setTemplate(SqlSessionTemplate template) public void setTemplate(SqlSessionTemplate template)
{ {
this.template = template; this.template = template;
@@ -313,6 +324,9 @@ public class DBQueryEngine implements QueryEngine
FilteringResultSet acceleratedNodeSelection(QueryOptions options, DBQuery dbQuery, NodePermissionAssessor permissionAssessor) FilteringResultSet acceleratedNodeSelection(QueryOptions options, DBQuery dbQuery, NodePermissionAssessor permissionAssessor)
{ {
// get list of stores from database
stores = nodeDAO.getStores();
List<Node> nodes = new ArrayList<>(); List<Node> nodes = new ArrayList<>();
int requiredNodes = computeRequiredNodesCount(options); int requiredNodes = computeRequiredNodesCount(options);
@@ -322,21 +336,16 @@ public class DBQueryEngine implements QueryEngine
@Override @Override
public void handleResult(ResultContext<? extends Node> context) public void handleResult(ResultContext<? extends Node> context)
{ {
doHandleResult(permissionAssessor, nodes, requiredNodes, context); if (!maxPermissionCheckEnabled && nodes.size() >= requiredNodes)
}
private void doHandleResult(NodePermissionAssessor permissionAssessor, List<Node> nodes,
int requiredNodes, ResultContext<? extends Node> context)
{
if (nodes.size() >= requiredNodes)
{ {
context.stop(); context.stop();
return; return;
} }
Node node = context.getResultObject(); Node node = context.getResultObject();
addStoreInfo(node);
boolean shouldCache = nodes.size() >= options.getSkipCount(); boolean shouldCache = shouldCache(options, nodes, requiredNodes);
if(shouldCache) if(shouldCache)
{ {
logger.debug("- selected node "+nodes.size()+": "+node.getUuid()+" "+node.getId()); logger.debug("- selected node "+nodes.size()+": "+node.getUuid()+" "+node.getId());
@@ -349,7 +358,14 @@ public class DBQueryEngine implements QueryEngine
if (permissionAssessor.isIncluded(node)) if (permissionAssessor.isIncluded(node))
{ {
nodes.add(shouldCache ? node : null); if (nodes.size() > requiredNodes)
{
nodes.add(node);
}
else
{
nodes.add(shouldCache ? node : null);
}
} }
if (permissionAssessor.shouldQuitChecks()) if (permissionAssessor.shouldQuitChecks())
@@ -358,6 +374,18 @@ public class DBQueryEngine implements QueryEngine
return; return;
} }
} }
private boolean shouldCache(QueryOptions options, List<Node> nodes, int requiredNodes)
{
if (nodes.size() > requiredNodes)
{
return false;
}
else
{
return nodes.size() >= options.getSkipCount();
}
}
}); });
int numberFound = nodes.size(); int numberFound = nodes.size();
@@ -457,4 +485,21 @@ public class DBQueryEngine implements QueryEngine
return value.getNodeRef(); return value.getNodeRef();
} }
} }
private void addStoreInfo(Node node)
{
StoreEntity storeEntity = node.getStore();
logger.debug("Adding store info for store id " + storeEntity.getId());
for (Pair<Long, StoreRef> storeRefPair : stores)
{
if (Objects.equals(storeEntity.getId(), storeRefPair.getFirst()))
{
StoreRef storeRef = storeRefPair.getSecond();
storeEntity.setIdentifier(storeRef.getIdentifier());
storeEntity.setProtocol(storeRef.getProtocol());
logger.debug("Added store info" + storeEntity.toString());
break;
}
}
}
} }

View File

@@ -41,9 +41,13 @@ import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper; import org.alfresco.util.EqualsHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class NodePermissionAssessor public class NodePermissionAssessor
{ {
protected static final Log logger = LogFactory.getLog(NodePermissionAssessor.class);
private final boolean isSystemReading; private final boolean isSystemReading;
private final boolean isAdminReading; private final boolean isAdminReading;
private final boolean isNullReading; private final boolean isNullReading;
@@ -138,24 +142,31 @@ public class NodePermissionAssessor
public void setMaxPermissionChecks(int maxPermissionChecks) public void setMaxPermissionChecks(int maxPermissionChecks)
{ {
this.maxPermissionChecks = maxPermissionChecks; if (maxPermissionChecks == Integer.MAX_VALUE)
{
this.maxPermissionChecks = maxPermissionChecks;
}
else
{
this.maxPermissionChecks = maxPermissionChecks + 1;
}
} }
public boolean shouldQuitChecks() public boolean shouldQuitChecks()
{ {
boolean result = false;
if (checksPerformed >= maxPermissionChecks) if (checksPerformed >= maxPermissionChecks)
{ {
result = true; logger.warn("Maximum permission checks exceeded (" + maxPermissionChecks + ")");
return true;
} }
if ((System.currentTimeMillis() - startTime) >= maxPermissionCheckTimeMillis) if ((System.currentTimeMillis() - startTime) >= maxPermissionCheckTimeMillis)
{ {
result = true; logger.warn("Maximum permission checks time exceeded (" + maxPermissionCheckTimeMillis + ")");
return true;
} }
return result; return false;
} }
public void setMaxPermissionCheckTimeMillis(long maxPermissionCheckTimeMillis) public void setMaxPermissionCheckTimeMillis(long maxPermissionCheckTimeMillis)

View File

@@ -153,6 +153,7 @@ system.cache.parentAssocs.limitFactor=8
system.acl.maxPermissionCheckTimeMillis=10000 system.acl.maxPermissionCheckTimeMillis=10000
# The maximum number of search results to perform permission checks against # The maximum number of search results to perform permission checks against
system.acl.maxPermissionChecks=1000 system.acl.maxPermissionChecks=1000
system.acl.maxPermissionCheckEnabled=false
# The maximum number of filefolder list results # The maximum number of filefolder list results
system.filefolderservice.defaultListMaxResults=5000 system.filefolderservice.defaultListMaxResults=5000

View File

@@ -126,6 +126,9 @@
<property name="maxPermissionCheckTimeMillis"> <property name="maxPermissionCheckTimeMillis">
<value>${system.acl.maxPermissionCheckTimeMillis}</value> <value>${system.acl.maxPermissionCheckTimeMillis}</value>
</property> </property>
<property name="maxPermissionCheckEnabled">
<value>${system.acl.maxPermissionCheckEnabled}</value>
</property>
</bean> </bean>
<bean id="search.dbQueryEngine" class="org.springframework.aop.framework.ProxyFactoryBean"> <bean id="search.dbQueryEngine" class="org.springframework.aop.framework.ProxyFactoryBean">

View File

@@ -159,6 +159,19 @@ public class DBQueryEngineTest
assertNodePresent(7, result); assertNodePresent(7, result);
} }
@Test
public void shouldResultSetLengthMatchTheAmountOfAllAccessibleNodesWhenMaxPermissionCheckEnabled()
{
withMaxItems(5);
prepareTemplate(dbQuery, createNodes(10));
when(assessor.isIncluded(any(Node.class))).thenReturn(true);
engine.setMaxPermissionCheckEnabled(true);
FilteringResultSet result = engine.acceleratedNodeSelection(options, dbQuery, assessor);
assertEquals(10, result.length());
}
@Test @Test
public void shouldNotConsiderInaccessibleNodesInResultSetWhenSkippingNodes() public void shouldNotConsiderInaccessibleNodesInResultSetWhenSkippingNodes()
{ {