[ 
https://issues.apache.org/jira/browse/JCR-3858?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14356682#comment-14356682
 ] 

Thomas Mueller commented on JCR-3858:
-------------------------------------

Patch for review:

{noformat}
Index: 
src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java  
(revision 1641780)
+++ src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java  
(working copy)
@@ -39,7 +39,7 @@
  * Implements the <code>QueryResult</code> interface.
  */
 public abstract class QueryResultImpl implements JackrabbitQueryResult {
-
+    
     /**
      * The logger instance for this class
      */
@@ -119,6 +119,8 @@
      * The maximum size of this result if limit >= 0
      */
     private final long limit;
+    
+    private final boolean sizeEstimate;
 
     /**
      * Creates a new query result. The concrete sub class is responsible for
@@ -146,6 +148,7 @@
             ColumnImpl[] columns, boolean documentOrder,
             long offset, long limit) throws RepositoryException {
         this.index = index;
+        this.sizeEstimate = index.getSizeEstimate();
         this.sessionContext = sessionContext;
         this.queryImpl = queryImpl;
         this.spellSuggestion = spellSuggestion;
@@ -258,10 +261,12 @@
             log.debug("getResults({}) limit={}", size, limit);
         }
         
-        // quick check
-        // if numResults is set, all relevant results have been fetched
-        if (numResults != -1) {
-            return;
+        if (!sizeEstimate) {
+            // quick check
+            // if numResults is set, all relevant results have been fetched
+            if (numResults != -1) {
+                return;
+            }
         }
 
         long maxResultSize = size;
@@ -291,7 +296,11 @@
             List<ScoreNode[]> offsetNodes = new ArrayList<ScoreNode[]>();
             if (resultNodes.isEmpty() && offset > 0) {
                 // collect result offset into dummy list
-                collectScoreNodes(result, offsetNodes, offset);
+                if (sizeEstimate) {
+                    collectScoreNodes(result, new ArrayList<ScoreNode[]>(), 
offset);                    
+                } else {
+                    collectScoreNodes(result, offsetNodes, offset);
+                }
             } else {
                 int start = resultNodes.size() + invalid + (int) offset;
                 result.skip(start);
@@ -303,24 +312,29 @@
             log.debug("retrieved ScoreNodes in {} ms ({})",
                     System.currentTimeMillis() - time, r3 - r2);
 
-            // update numResults if all results have been fetched 
-            // if resultNodes.getSize() is strictly smaller than 
maxResultSize, it means that all results have been fetched
-            int resultSize = resultNodes.size();
-            if (resultSize < maxResultSize) {
-                if (resultNodes.isEmpty()) {
-                    // if there's no result nodes, the actual totalResults if 
smaller or equals than the offset
-                    totalResults = offsetNodes.size();
-                    numResults = 0;
+            if (sizeEstimate) {
+                // update numResults
+                numResults = result.getSize();                
+            } else {
+                // update numResults if all results have been fetched 
+                // if resultNodes.getSize() is strictly smaller than 
maxResultSize, it means that all results have been fetched
+                int resultSize = resultNodes.size();
+                if (resultSize < maxResultSize) {
+                    if (resultNodes.isEmpty()) {
+                        // if there's no result nodes, the actual totalResults 
if smaller or equals than the offset
+                        totalResults = offsetNodes.size();
+                        numResults = 0;
+                    }
+                    else {
+                        totalResults = resultSize + (int) offset;
+                        numResults = resultSize;
+                    }
                 }
-                else {
-                    totalResults = resultSize + (int) offset;
-                    numResults = resultSize;
+                else if (resultSize == limit) {
+                    // if there's "limit" results, we can't know the total 
size (which may be greater), but the result size is the limit
+                    numResults = (int) limit;
                 }
             }
-            else if (resultSize == limit) {
-                // if there's "limit" results, we can't know the total size 
(which may be greater), but the result size is the limit
-                numResults = (int) limit;
-            }
         } catch (IOException e) {
             throw new RepositoryException(e);
         } finally {
@@ -393,11 +407,23 @@
      * will get get if you don't set any limit or offset. This method may 
return
      * <code>-1</code> if the total size is unknown.
      * <p>
+     * If the "sizeEstimate" options is enabled:
+     * Keep in mind that this number may get smaller if nodes are found in
+     * the result set which the current session has no permission to access.
+     * This might be a security problem.
      *
      * @return the total number of hits.
      */
     public int getTotalSize() {
-        return totalResults;
+        if (sizeEstimate) {
+            if (numResults == -1) {
+                return -1;
+            } else {
+                return numResults - invalid;
+            }
+        } else {
+            return totalResults;
+        }
     }
 
     private final class LazyScoreNodeIteratorImpl implements ScoreNodeIterator 
{
@@ -448,9 +474,26 @@
 
         /**
          * {@inheritDoc}
+         * <p/>
+         * If the "sizeEstimate" options is enabled:
+         * This value may shrink when the query result encounters non-existing
+         * nodes or the session does not have access to a node.
          */
         public long getSize() {
-            return numResults;
+            if (sizeEstimate) {
+                int total = getTotalSize();
+                if (total == -1) {
+                    return -1;
+                }
+                long size = offset > total ? 0 : total - offset;
+                if (limit >= 0 && size > limit) {
+                    return limit;
+                } else {
+                    return size;
+                }                
+            } else {
+                return numResults;
+            }
         }
 
         /**
@@ -504,9 +547,16 @@
             while (next == null) {
                 if (nextPos >= resultNodes.size()) {
                     // quick check if there are more results at all
-                    // if numResults is set, all relevant results have been 
fetched
-                    if (numResults != -1) {
-                        break;
+                    if (sizeEstimate) {
+                        // this check is only possible if we have numResults
+                        if (numResults != -1 && (nextPos + invalid) >= 
numResults) {
+                            break;
+                        }
+                    } else {
+                        // if numResults is set, all relevant results have 
been fetched
+                        if (numResults != -1) {
+                            break;
+                        }
                     }
 
                     // fetch more results
Index: src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java      
(revision 1641780)
+++ src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java      
(working copy)
@@ -386,6 +386,14 @@
      * Default value is: <code>false</code>.
      */
     private boolean supportHighlighting = false;
+    
+    /**
+     * If enabled, NodeIterator.getSize() may report a larger value than the
+     * actual result. This value may shrink when the query result encounters
+     * non-existing nodes or the session does not have access to a node. This
+     * might be a security problem.
+     */
+    private boolean sizeEstimate = false;
 
     /**
      * The excerpt provider class. Implements {@link ExcerptProvider}.
@@ -2178,6 +2186,27 @@
     public long getExtractorTimeout() {
         return extractorTimeout;
     }
+    
+    /**
+     * If enabled, NodeIterator.getSize() may report a larger value than the
+     * actual result. This value may shrink when the query result encounters
+     * non-existing nodes or the session does not have access to a node. This
+     * might be a security problem.
+     * 
+     * @param b <code>true</code> to enable
+     */
+    public void setSizeEstimate(boolean b) {
+        this.sizeEstimate = b;
+    }
+    
+    /**
+     * Get the size estimate setting.
+     * 
+     * @return the setting
+     */
+    public boolean getSizeEstimate() {
+        return sizeEstimate;
+    }
 
     /**
      * If set to <code>true</code> additional information is stored in the 
index
{noformat}

> NodeIterator.getSize(): compatibility with Jackrabbit 2.5
> ---------------------------------------------------------
>
>                 Key: JCR-3858
>                 URL: https://issues.apache.org/jira/browse/JCR-3858
>             Project: Jackrabbit Content Repository
>          Issue Type: New Feature
>    Affects Versions: 2.6.2, 2.7
>            Reporter: Thomas Mueller
>            Assignee: Thomas Mueller
>
> In Jackrabbit 2.5 and older, the query result set (NodeIterator.getSize()) 
> was an estimation that sometimes included nodes that are not visible for the 
> current user.
> This is a possible security problem. The behavior was changed (and the 
> security problem fixed) in JCR-3402. However, this is an incompatibility with 
> Jackrabbit 2.5.
> I suggest to make this configurable in workspace.xml / repository.xml (or a 
> system property, if that turns out to be too complicated). The default is the 
> current (secure) behavior, with the option to use the old variant.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to